openapi: 3.1.0 info: title: AiSOC Platform API description: AiSOC — open-source AI Security Operations Center. Autonomous threat detection, investigation, and response. version: 0.1.0 paths: /api/v1/auth/login: post: tags: - auth summary: Login description: Authenticate with email/password, return JWT tokens. operationId: login_api_v1_auth_login_post requestBody: content: application/json: schema: $ref: '#/components/schemas/LoginRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TokenResponse' '202': description: The password was accepted and a second factor is outstanding. The body carries `mfa_token` plus either `mfa_required` (complete it at `POST /auth/mfa/verify`) or `mfa_enrollment_required` (the tenant requires a factor this account has not enrolled; enrol at `POST /auth/mfa/enroll/begin`). No session is issued. content: application/json: example: mfa_required: true mfa_token: eyJ… methods: - totp - recovery_code '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/auth/sso/status: get: tags: - auth summary: Sso Status description: 'What the login screen asks before it offers SSO. Public: it reveals only that SSO is offered, under what label, and whether password sign-in remains open -- never issuers, client ids, endpoints or secrets.' operationId: sso_status_api_v1_auth_sso_status_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Sso Status Api V1 Auth Sso Status Get /api/v1/auth/refresh: post: tags: - auth summary: Refresh Token description: Refresh access token using a valid refresh token. operationId: refresh_token_api_v1_auth_refresh_post requestBody: content: application/json: schema: $ref: '#/components/schemas/RefreshRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TokenResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/auth/me: get: tags: - auth summary: Get Me description: Get current authenticated user info. operationId: get_me_api_v1_auth_me_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/UserMeResponse' security: - HTTPBearer: [] /api/v1/auth/me/preferences: patch: tags: - auth summary: Patch Me Preferences description: 'Merge user preferences (e.g. theme) into the stored JSONB column. Only the keys supplied in the request body are updated; all other existing keys are preserved. This lets the frontend evolve independent preference namespaces without overwriting each other.' operationId: patch_me_preferences_api_v1_auth_me_preferences_patch requestBody: content: application/json: schema: $ref: '#/components/schemas/PreferencesPatch' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/UserMeResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/auth/mfa/status: get: tags: - auth - mfa summary: Mfa Status description: Whether this user holds a second factor, and whether they must. operationId: mfa_status_api_v1_auth_mfa_status_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MfaStatus' security: - HTTPBearer: [] /api/v1/auth/mfa/enroll/begin: post: tags: - auth - mfa summary: Enroll Begin description: 'Mint a secret and return it once, with the URI an app scans. Re-enrolling replaces an *unconfirmed* row and refuses a confirmed one: silently rotating a working factor would turn a mis-click into a lockout, and `DELETE /auth/mfa` is the deliberate way to remove one.' operationId: enroll_begin_api_v1_auth_mfa_enroll_begin_post responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/EnrollBegin' security: - HTTPBearer: [] /api/v1/auth/mfa/enroll/confirm: post: tags: - auth - mfa summary: Enroll Confirm description: 'Prove the secret arrived, then hand over the recovery codes. Confirmation is what makes the row a credential. Without it, an enrolment begun on a device that never received the QR code would lock the user out at their next sign-in.' operationId: enroll_confirm_api_v1_auth_mfa_enroll_confirm_post requestBody: content: application/json: schema: $ref: '#/components/schemas/ConfirmRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/EnrollConfirmed' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/auth/mfa/verify: post: tags: - auth - mfa summary: Verify description: Complete a sign-in that `POST /auth/login` answered with 202. operationId: verify_api_v1_auth_mfa_verify_post requestBody: content: application/json: schema: $ref: '#/components/schemas/VerifyRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SessionTokens' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/auth/mfa/webauthn/challenge: post: tags: - auth - mfa summary: Webauthn Challenge description: 'Assertion options for a user part-way through a sign-in. The user is taken from the signed `mfa_token`, never from the body: an endpoint that let a caller name whose credentials to list would answer "does this address hold a passkey" to anybody who asked.' operationId: webauthn_challenge_api_v1_auth_mfa_webauthn_challenge_post requestBody: content: application/json: schema: $ref: '#/components/schemas/WebAuthnChallengeRequest' required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Webauthn Challenge Api V1 Auth Mfa Webauthn Challenge Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/auth/mfa/webauthn/verify: post: tags: - auth - mfa summary: Webauthn Verify description: 'Complete a sign-in with a security key or passkey. The credential must belong to the user the challenge names. Without that check anybody holding **any** enrolled authenticator could discharge **anybody''s** challenge, which is the whole control.' operationId: webauthn_verify_api_v1_auth_mfa_webauthn_verify_post requestBody: content: application/json: schema: $ref: '#/components/schemas/WebAuthnVerifyRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SessionTokens' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/auth/mfa: delete: tags: - auth - mfa summary: Disable description: 'Remove your own second factor, proving you still hold it. A current code is required. Without it, a stolen session could strip the factor it was supposed to be protected by, which makes the factor worth exactly as much as the session.' operationId: disable_api_v1_auth_mfa_delete requestBody: content: application/json: schema: $ref: '#/components/schemas/DisableRequest' required: true responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/auth/mfa/reset/{user_id}: post: tags: - auth - mfa summary: Reset description: "Remove somebody else's second factor. The lost-phone path.\n\n\ Three constraints, each of which is the reason an administrative reset\nis\ \ normally the weakest link in an MFA deployment:\n\n* the target must be\ \ in the caller's own tenant, resolved from the\n authenticated principal\ \ and never from the request;\n* a `reason` is required and lands in the audit\ \ row, because \"an admin\n reset it\" without one is not an answer to \"\ who removed this and why\";\n* an administrator may not reset **their own**\ \ factor here. That would\n be a self-disable with no code, bypassing `DELETE\ \ /auth/mfa`." operationId: reset_api_v1_auth_mfa_reset__user_id__post security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string format: uuid title: User Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ResetRequest' responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: type: string title: Response Reset Api V1 Auth Mfa Reset User Id Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/auth/mfa/policy: get: tags: - auth - mfa summary: Get Policy description: 'Readable by any member: a user is entitled to know what their own tenant requires of them.' operationId: get_policy_api_v1_auth_mfa_policy_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MfaPolicy' security: - HTTPBearer: [] put: tags: - auth - mfa summary: Put Policy description: 'Require, or stop requiring, a second factor across this tenant. The tenant is the caller''s, never a parameter. A row is written only when an administrator asks for one, so a tenant that has never visited this endpoint has no row and is not enforced. `require_for_roles` names roles that must hold a factor even when the tenant-wide switch is off. Every name is checked against the role vocabulary before it is stored: a typo would otherwise be accepted, match nobody''s role, and leave an administrator looking at a policy page that says a role is enforced while it is not — the quiet direction, and the one worth refusing loudly. The vocabulary comes from `role_grants.known_roles()` rather than from `ROLE_PERMISSIONS` directly. This route decides nothing about permissions and must not be able to: a route that reads the static map is how a second permission model gets rebuilt, which `scripts/check_one_permission_model.py` exists to prevent.' operationId: put_policy_api_v1_auth_mfa_policy_put requestBody: content: application/json: schema: $ref: '#/components/schemas/MfaPolicy' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MfaPolicy' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/api-keys: get: tags: - api-keys summary: List Api Keys description: List all API keys for the current tenant (raw keys are never returned). operationId: list_api_keys_api_v1_api_keys_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/ApiKeyOut' type: array title: Response List Api Keys Api V1 Api Keys Get security: - HTTPBearer: [] post: tags: - api-keys summary: Create Api Key description: 'Create a new scoped API key for the current tenant. The raw key value is returned **only once** in this response.' operationId: create_api_key_api_v1_api_keys_post requestBody: content: application/json: schema: $ref: '#/components/schemas/CreateApiKeyRequest' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CreateApiKeyResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/api-keys/{key_id}: get: tags: - api-keys summary: Get Api Key description: Get metadata for a specific API key. operationId: get_api_key_api_v1_api_keys__key_id__get security: - HTTPBearer: [] parameters: - name: key_id in: path required: true schema: type: string format: uuid title: Key Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ApiKeyOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' patch: tags: - api-keys summary: Update Api Key description: Update an API key's name, scopes, or expiry. operationId: update_api_key_api_v1_api_keys__key_id__patch security: - HTTPBearer: [] parameters: - name: key_id in: path required: true schema: type: string format: uuid title: Key Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateApiKeyRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ApiKeyOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - api-keys summary: Revoke Api Key description: Revoke (deactivate) an API key. The key can no longer be used for authentication. operationId: revoke_api_key_api_v1_api_keys__key_id__delete security: - HTTPBearer: [] parameters: - name: key_id in: path required: true schema: type: string format: uuid title: Key Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/alerts/submit: post: tags: - alerts summary: Submit Alert description: "Submit one alert directly from an OCSF event batch.\n\nThis is\ \ the destination for `aisoc submit ` and the local-dev fast\npath documented\ \ in the quickstart. We deliberately bypass the Kafka\ndetect/correlate/fuse\ \ pipeline (which fresh clones don't run by default)\nso that a hand-crafted\ \ fixture lands in the database — and therefore in\n``GET /api/v1/alerts``\ \ and the web console — within the same second.\n\n**Hardening (Batch 8 /\ \ H-5)**:\n\n* **Payload caps** — events are first run through\n :func:`app.services.event_sanitiser.sanitise_event_batch`,\ \ which\n enforces ``AISOC_SUBMIT_MAX_EVENTS`` (default 200), per-event size\n\ \ ``AISOC_SUBMIT_MAX_EVENT_BYTES`` (default 64 KiB), and total batch\n size\ \ ``AISOC_SUBMIT_MAX_TOTAL_BYTES`` (default 1 MiB). Violations\n return ``413\ \ Payload Too Large`` rather than the 200 OK + truncated\n alert we used\ \ to silently emit.\n* **Secret redaction** — the sanitiser also redacts keys\ \ matching\n common secret patterns (``password``, ``token``, ``api_key``,\ \ …)\n *before* the payload is persisted to ``alerts.raw_event``, so a\n\ \ leaky connector cannot smuggle bearer tokens into the alert table\n where\ \ they would be visible to every analyst with ``alerts:read``.\n* **Idempotency**\ \ — when the client supplies an ``Idempotency-Key``\n header, the resulting\ \ alert is keyed ``(tenant_id, idempotency_key)``\n via a partial unique\ \ index (migration ``044``). A retry of the\n same key resolves to the existing\ \ row and returns HTTP ``200``\n instead of creating a duplicate, so at-least-once\ \ connectors and\n the ``aisoc submit`` CLI can retry safely on network blips.\n\ * **Timestamp bounds** — event timestamps outside the configured\n retention\ \ window are clamped (see\n :mod:`app.services.timestamp_bounds`); the resulting\ \ alert is\n tagged ``ts-clamped`` so analysts can see the connector misbehaved.\n\ \nAuthorisation: requires ``alerts:write``. In development mode the\nauth\ \ bypass in :mod:`app.api.v1.deps` resolves to the deterministic\ndemo tenant,\ \ so the documented ``aisoc submit`` CLI works against a\nfresh clone with\ \ no token plumbing." operationId: submit_alert_api_v1_alerts_submit_post security: - HTTPBearer: [] parameters: - name: Idempotency-Key in: header required: false schema: anyOf: - type: string - type: 'null' description: Optional opaque client-supplied key (8–128 chars, [A-Za-z0-9._:-]). When supplied, a retry of the same key for the same tenant returns the existing alert with HTTP 200 instead of creating a duplicate. title: Idempotency-Key description: Optional opaque client-supplied key (8–128 chars, [A-Za-z0-9._:-]). When supplied, a retry of the same key for the same tenant returns the existing alert with HTTP 200 instead of creating a duplicate. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AlertSubmitRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AlertResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/alerts: get: tags: - alerts summary: List Alerts description: 'List alerts for the current tenant with filtering and pagination. `min_confidence` / `confidence_label` let the queue workbench (PR-5) and /alerts page filter on the fusion-emitted confidence signal (W3). Alerts that pre-date the confidence column will have NULL and therefore won''t match either filter — that''s intentional; analysts who care about confidence should only see alerts that actually carry the signal. `sort` is `newest` (created_at descending) or `priority` (severity descending, then created_at descending). It defaults to `newest`: this endpoint has generated SDK clients and "page one is the newest rows" is part of what they were built against, so changing the order silently would alter what every existing caller reads *without* altering the schema — which `openapi-breaking.yml` cannot detect. The console asks for `priority`, because a noisy low-severity source emitting faster than an analyst pages pushes a critical off page one and keeps it there. `limit` is an accepted spelling of `page_size`. Supplying both with different values is a 400.' operationId: list_alerts_api_v1_alerts_get security: - HTTPBearer: [] 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: 200 minimum: 1 default: 25 title: Page Size - name: limit in: query required: false schema: anyOf: - type: integer maximum: 200 minimum: 1 - type: 'null' description: Alias for `page_size`, bounded identically. Supplying both with different values is a 400. title: Limit description: Alias for `page_size`, bounded identically. Supplying both with different values is a 400. - name: severity in: query required: false schema: anyOf: - type: string - type: 'null' title: Severity - name: status in: query required: false schema: anyOf: - type: string - type: 'null' title: Status - name: category in: query required: false schema: anyOf: - type: string - type: 'null' title: Category - name: assigned_to_me in: query required: false schema: type: boolean default: false title: Assigned To Me - name: search in: query required: false schema: anyOf: - type: string - type: 'null' title: Search - name: min_confidence in: query required: false schema: anyOf: - type: integer maximum: 100 minimum: 0 - type: 'null' title: Min Confidence - name: confidence_label in: query required: false schema: anyOf: - type: string - type: 'null' title: Confidence Label - name: sort in: query required: false schema: enum: - newest - priority type: string default: newest title: Sort responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AlertListResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/alerts/stats: get: tags: - alerts summary: Get Alert Stats description: Get alert statistics for the dashboard. operationId: get_alert_stats_api_v1_alerts_stats_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AlertStatsResponse' security: - HTTPBearer: [] /api/v1/alerts/queue: get: tags: - alerts summary: Get Alert Queue description: 'Investigation Queue workbench feed. Returns the prioritised list of alerts the analyst should work on next: alerts assigned to them, followed by unassigned critical/high alerts, ordered by SLA due time. The endpoint also returns ``counts`` for both buckets unconditionally so the topbar badge and the workbench tabs can render without an extra round-trip.' operationId: get_alert_queue_api_v1_alerts_queue_get security: - HTTPBearer: [] parameters: - name: owner in: query required: false schema: enum: - me - unassigned - all type: string default: all title: Owner - name: period in: query required: false schema: enum: - 24h - 7d - 30d - all type: string default: all title: Period - 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: 200 minimum: 1 default: 50 title: Page Size responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/QueueResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/alerts/{alert_id}: get: tags: - alerts summary: Get Alert description: "Get a single alert by ID, enriched with Investigation Rail data.\n\ \nThe endpoint returns the standard alert fields plus everything the\nrail\ \ needs to render in one shot:\n\n* a deterministic ``narrative`` (lazily\ \ filled and persisted if the\n row was created before fusion started emitting\ \ it),\n* ``related_entities`` grouped for pivot,\n* a compact ``mini_timeline``,\n\ * normalised ``recommended_actions``.\n\nThe lazy-fill is best-effort: if\ \ the narrative builder ever raises\nwe log and return the alert with ``narrative=None``\ \ so the page\nkeeps rendering. The frontend already handles the null case." operationId: get_alert_api_v1_alerts__alert_id__get security: - HTTPBearer: [] parameters: - name: alert_id in: path required: true schema: type: string format: uuid title: Alert Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AlertDetailResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' patch: tags: - alerts summary: Update Alert description: 'Update alert status, priority, tags, assignment, or case link. Emits an `alerts:update` audit event carrying a before/after payload for the fields this call changed.' operationId: update_alert_api_v1_alerts__alert_id__patch security: - HTTPBearer: [] parameters: - name: alert_id in: path required: true schema: type: string format: uuid title: Alert Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AlertUpdateRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AlertResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/alerts/{alert_id}/escalate: post: tags: - alerts summary: Escalate Alert description: Escalate an alert (raises severity by one level). operationId: escalate_alert_api_v1_alerts__alert_id__escalate_post security: - HTTPBearer: [] parameters: - name: alert_id in: path required: true schema: type: string format: uuid title: Alert Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AlertResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/alerts/{alert_id}/claim: post: tags: - alerts summary: Claim Alert Endpoint description: 'Atomically claim an unassigned alert for the current user. Returns ``409 Conflict`` if the alert is already assigned to someone else — the claim is a compare-and-set on ``assigned_to_id``, so two analysts racing for the same alert never both win.' operationId: claim_alert_endpoint_api_v1_alerts__alert_id__claim_post security: - HTTPBearer: [] parameters: - name: alert_id in: path required: true schema: type: string format: uuid title: Alert Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AlertResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/alerts/{alert_id}/snooze: post: tags: - alerts summary: Snooze Alert description: 'Defer an alert for a fixed window from the mobile responder PWA. Either ``duration_minutes`` or ``until`` must be supplied. The alert re-surfaces in the queue once ``snoozed_until`` is in the past.' operationId: snooze_alert_api_v1_alerts__alert_id__snooze_post security: - HTTPBearer: [] parameters: - name: alert_id in: path required: true schema: type: string format: uuid title: Alert Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AlertSnoozeRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AlertResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/alerts/bulk-close: post: tags: - alerts summary: Bulk Close Alerts Endpoint description: "Close many alerts in one call, with a preflight guard and a snapshot.\n\ \n* Pass ``alert_ids`` for an explicit set, or filters\n (``statuses``/``severities``/``categories``/``connector_types``/\n\ \ ``older_than``) for a class of alerts. A filter without ``older_than``\n\ \ only matches still-open rows.\n* **Dry-run for free:** the first call with\ \ a ``max_count`` below the\n real match count returns ``409`` carrying ``matched_count``\ \ — raise\n ``max_count`` past it to execute. Nothing is written on the 409.\n\ * Every affected row is snapshotted into a dedicated backup table\n (returned\ \ in the body) before the UPDATE commits.\n* One audit entry covers the batch\ \ (with the backup table name so the\n restore target is recorded next to\ \ the action).\n\nTypical noise sweep::\n\n POST /api/v1/alerts/bulk-close\n\ \ {\"categories\": [\"malware\", \"vulnerability\"],\n \"severities\"\ : [\"low\", \"informational\"],\n \"older_than\": \"2026-10-05T00:00:00Z\"\ ,\n \"max_count\": 5000, \"comment\": \"quarterly noise sweep\"}" operationId: bulk_close_alerts_endpoint_api_v1_alerts_bulk_close_post requestBody: content: application/json: schema: $ref: '#/components/schemas/AlertBulkCloseRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AlertBulkCloseResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/alerts/{alert_id}/explain: post: tags: - alerts summary: Generate a structured AI explanation for an alert description: 'Return a structured explanation for one alert. This is the *single-shot* counterpart to the agent service''s NDJSON stream. Use this when the client is a non-interactive consumer (PDF builder, SOAR playbook, mobile app) or when the UI just wants the final payload without rendering tokens.' operationId: explain_alert_api_v1_alerts__alert_id__explain_post security: - HTTPBearer: [] parameters: - name: alert_id in: path required: true schema: type: string format: uuid title: Alert Id responses: '200': description: Structured explanation payload (rule lineage, MITRE, FPR, actions, summary) content: application/json: schema: type: object additionalProperties: true title: Response Explain Alert Api V1 Alerts Alert Id Explain Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/alerts/{alert_id}/source-writeback: post: tags: - alerts summary: Write an AiSOC verdict back to the source finding description: 'Project ``disposition`` onto every source finding behind this alert. The response distinguishes four things a caller could otherwise confuse: ``mode`` (live / dry_run / disabled), ``executed`` (whether a vendor call actually ran), each outcome''s ``status``, and the per-vendor ``detail`` explaining a refusal. A dry run reports ``executed: false`` and a detail that begins ``DRY RUN``.' operationId: write_back_api_v1_alerts__alert_id__source_writeback_post security: - HTTPBearer: [] parameters: - name: alert_id in: path required: true schema: type: string format: uuid title: Alert Id - name: x-aisoc-service-token in: header required: false schema: anyOf: - type: string - type: 'null' title: X-Aisoc-Service-Token requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SourceWritebackRequest' responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Write Back Api V1 Alerts Alert Id Source Writeback Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/alerts/{alert_id}/source-links: get: tags: - alerts summary: Which vendor findings produced this alert description: 'The reconciliation view: source findings and their last writeback.' operationId: source_links_api_v1_alerts__alert_id__source_links_get security: - HTTPBearer: [] parameters: - name: alert_id in: path required: true schema: type: string format: uuid title: Alert Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/SourceLinkResponse' title: Response Source Links Api V1 Alerts Alert Id Source Links Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases: get: tags: - cases summary: List cases operationId: list_cases_api_v1_cases_get security: - HTTPBearer: [] parameters: - name: status in: query required: false schema: anyOf: - type: string - type: 'null' title: Status - name: severity in: query required: false schema: anyOf: - type: string - type: 'null' title: Severity - name: assignee in: query required: false schema: anyOf: - type: string - type: 'null' title: Assignee - 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: array items: $ref: '#/components/schemas/CaseResponse' title: Response List Cases Api V1 Cases Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - cases summary: Create case operationId: create_case_api_v1_cases_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateCaseRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CaseResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}: get: tags: - cases summary: Get case operationId: get_case_api_v1_cases__case_id__get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CaseResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' patch: tags: - cases summary: Update case operationId: update_case_api_v1_cases__case_id__patch security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateCaseRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CaseResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/reopen: post: tags: - cases summary: Reopen a closed case description: "Bring a terminal case back, deliberately and on the record.\n\n\ Its own route rather than a backward edge in `TRANSITIONS`. The forward-only\n\ machine is what makes \"this case was closed\" mean something, and adding\n\ `closed -> investigating` to the table would let an ordinary `PATCH` --\n\ a title edit that happens to carry a status -- walk a case backwards\nsilently.\ \ `PATCH` stays forward-only.\n\nThree things separate this from a status\ \ change:\n\n* a reason is required, because an unexplained reopen is exactly\ \ what an\n auditor asks about six months later;\n* `reopen_count` increments,\ \ so a case reopened four times is\n distinguishable from one reopened once;\n\ * `closed_at` and `resolved_at` are cleared, because leaving them set\n would\ \ make the case terminal and active at the same time -- and every\n closure\ \ metric reads those columns." operationId: reopen_case_api_v1_cases__case_id__reopen_post security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ReopenCaseRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CaseResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/alerts: post: tags: - cases summary: Link alerts to a case operationId: add_alerts_api_v1_cases__case_id__alerts_post security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AddAlertsRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CaseResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/observables: post: tags: - cases summary: Update observable graph operationId: update_observables_api_v1_cases__case_id__observables_post security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateObservablesRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CaseResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/comments: post: tags: - cases summary: Add comment operationId: add_comment_api_v1_cases__case_id__comments_post security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AddCommentRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CommentResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' get: tags: - cases summary: List case comments operationId: list_comments_api_v1_cases__case_id__comments_get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/CommentResponse' title: Response List Comments Api V1 Cases Case Id Comments Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/notes: get: tags: - cases summary: List case notes (alias of /comments) operationId: list_notes_api_v1_cases__case_id__notes_get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/CommentResponse' title: Response List Notes Api V1 Cases Case Id Notes Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - cases summary: Add case note (alias of /comments) operationId: add_note_api_v1_cases__case_id__notes_post security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AddCommentRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CommentResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/evidence: get: tags: - cases summary: Export evidence chain report operationId: evidence_report_api_v1_cases__case_id__evidence_get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/EvidenceReport' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/timeline: get: tags: - cases summary: Case activity timeline operationId: case_timeline_api_v1_cases__case_id__timeline_get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/app__api__v1__endpoints__cases__TimelineResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/tasks: get: tags: - cases summary: List case tasks operationId: list_tasks_api_v1_cases__case_id__tasks_get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/TaskResponse' title: Response List Tasks Api V1 Cases Case Id Tasks Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - cases summary: Create task operationId: create_task_api_v1_cases__case_id__tasks_post security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateTaskRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TaskResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/tasks/{task_id}: patch: tags: - cases summary: Update task operationId: update_task_api_v1_cases__case_id__tasks__task_id__patch security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id - name: task_id in: path required: true schema: type: string format: uuid title: Task Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateTaskRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TaskResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/investigate: post: tags: - cases summary: Launch investigation for case operationId: case_investigate_api_v1_cases__case_id__investigate_post security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/InvestigateRequest' responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Case Investigate Api V1 Cases Case Id Investigate Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/investigations: get: tags: - cases summary: List investigation runs for a case description: 'List all investigation runs for a case. The agents service is the source of truth. When it''s unreachable we return an empty list rather than 503 so the case detail page still renders.' operationId: list_case_investigations_api_v1_cases__case_id__investigations_get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response List Case Investigations Api V1 Cases Case Id Investigations Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/investigations/{run_id}: get: tags: - cases summary: Get investigation run description: One investigation run belonging to this case, in the caller's tenant. operationId: case_investigation_run_api_v1_cases__case_id__investigations__run_id__get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id - name: run_id in: path required: true schema: type: string title: Run Id responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Case Investigation Run Api V1 Cases Case Id Investigations Run Id Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/investigations/{run_id}/report.md: get: tags: - cases summary: Markdown incident report for one investigation run description: The Markdown report the case workspace renders. operationId: case_investigation_report_md_api_v1_cases__case_id__investigations__run_id__report_md_get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id - name: run_id in: path required: true schema: type: string title: Run Id responses: '200': description: Successful Response content: text/plain: schema: type: string '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/summary: get: tags: - cases summary: Download the per-case auto-summary (JSON or HTML) operationId: case_auto_summary_api_v1_cases__case_id__summary_get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id - name: format in: query required: false schema: enum: - json - html type: string description: Response format. ``json`` returns the structured CaseAutoSummary; ``html`` returns a print-ready report (use the browser's Save-as-PDF affordance for archival). default: json title: Format description: Response format. ``json`` returns the structured CaseAutoSummary; ``html`` returns a print-ready report (use the browser's Save-as-PDF affordance for archival). responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/postmortem: get: tags: - cases summary: Download the per-case blameless post-mortem (JSON or HTML) description: 'Generate a deterministic blameless post-mortem for one case. Distinct from ``/summary``: the summary is a snapshot of *what the case looks like right now*; the post-mortem is a retrospective oriented around *what happened, when did we find out, what did we do, and what should we change*. Both are deterministic — same case state in, same artefact out.' operationId: case_auto_postmortem_api_v1_cases__case_id__postmortem_get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id - name: format in: query required: false schema: enum: - json - html type: string description: Response format. ``json`` returns the structured CasePostmortem; ``html`` returns a print-ready blameless retrospective (use the browser's Save-as-PDF affordance for the runbook archive). default: json title: Format description: Response format. ``json`` returns the structured CasePostmortem; ``html`` returns a print-ready blameless retrospective (use the browser's Save-as-PDF affordance for the runbook archive). responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/investigations/{run_id}/report.pdf: get: tags: - cases summary: Download investigation PDF report operationId: case_investigation_pdf_api_v1_cases__case_id__investigations__run_id__report_pdf_get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id - name: run_id in: path required: true schema: type: string title: Run Id responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/related: get: tags: - cases summary: List related cases (by alert/observable overlap) description: 'Return other cases that share alerts or observables with this case. This is intentionally a lightweight heuristic — we look for cases that overlap on `alert_ids` or share an observable IP/host. It''s the smallest thing the case detail page needs to stop 404''ing.' operationId: list_related_cases_api_v1_cases__case_id__related_get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response List Related Cases Api V1 Cases Case Id Related Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/case-depth/alerts/merge: post: tags: - case-depth summary: Merge Alerts Route description: Fold duplicate alerts into one. Every row survives. operationId: merge_alerts_route_api_v1_case_depth_alerts_merge_post requestBody: content: application/json: schema: $ref: '#/components/schemas/MergeRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MergeResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/case-depth/cases/merge: post: tags: - case-depth summary: Merge Cases Route description: Fold duplicate cases into one. Alerts follow; both rows survive. operationId: merge_cases_route_api_v1_case_depth_cases_merge_post requestBody: content: application/json: schema: $ref: '#/components/schemas/MergeRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MergeResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/case-depth/alerts/bulk-triage: post: tags: - case-depth summary: Bulk Triage Route description: 'Apply one triage decision to many alerts, with a preflight count. A match larger than ``max_count`` is a **409 carrying the real figure** and nothing is written — the same contract ``POST /alerts/bulk-close`` uses, deliberately, because a second bulk path with weaker guarantees is how the weaker one becomes the one everybody reaches for.' operationId: bulk_triage_route_api_v1_case_depth_alerts_bulk_triage_post requestBody: content: application/json: schema: $ref: '#/components/schemas/BulkTriageRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/BulkTriageResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/case-depth/custom-fields: get: tags: - case-depth summary: List Custom Fields operationId: list_custom_fields_api_v1_case_depth_custom_fields_get security: - HTTPBearer: [] parameters: - name: include_inactive in: query required: false schema: type: boolean default: false title: Include Inactive responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/CustomFieldOut' title: Response List Custom Fields Api V1 Case Depth Custom Fields Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - case-depth summary: Create Custom Field operationId: create_custom_field_api_v1_case_depth_custom_fields_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CustomFieldIn' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CustomFieldOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/case-depth/custom-fields/{field_id}: patch: tags: - case-depth summary: Update Custom Field operationId: update_custom_field_api_v1_case_depth_custom_fields__field_id__patch security: - HTTPBearer: [] parameters: - name: field_id in: path required: true schema: type: string format: uuid title: Field Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CustomFieldPatch' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CustomFieldOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - case-depth summary: Retire Custom Field description: 'Retire, never delete: values already written stay readable and attributable.' operationId: retire_custom_field_api_v1_case_depth_custom_fields__field_id__delete security: - HTTPBearer: [] parameters: - name: field_id in: path required: true schema: type: string format: uuid title: Field Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CustomFieldOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/case-depth/cases/{case_id}/custom-values: get: tags: - case-depth summary: Get Custom Values description: 'Every active field, with this case''s value or an empty one. Returns the *fields*, not the stored rows, so a field defined after the case was created still renders — the alternative is a form that silently omits whatever was added last week.' operationId: get_custom_values_api_v1_case_depth_cases__case_id__custom_values_get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string format: uuid title: Case Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/CustomValueOut' title: Response Get Custom Values Api V1 Case Depth Cases Case Id Custom Values Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' put: tags: - case-depth summary: Set Custom Values operationId: set_custom_values_api_v1_case_depth_cases__case_id__custom_values_put security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string format: uuid title: Case Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CustomValuesIn' responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/CustomValueOut' title: Response Set Custom Values Api V1 Case Depth Cases Case Id Custom Values Put '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/case-depth/workload: get: tags: - case-depth summary: Workload description: 'Per-analyst open, closed, overdue and mean time to close. Every number is counted by this request over rows it selected. An analyst who has closed nothing gets ``mean_hours_to_close: null`` and the console renders that as "no data" — never ``0.0``, which reads as "instant".' operationId: workload_api_v1_case_depth_workload_get security: - HTTPBearer: [] parameters: - name: days in: query required: false schema: type: integer maximum: 365 minimum: 1 default: 30 title: Days responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Workload Api V1 Case Depth Workload Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/cases/{case_id}/attack-chain: get: tags: - cases summary: Get Attack Chain description: 'Return the ranked attack-chain timeline for the case''s seed alert. The seed alert is the earliest ``alert.event_time`` linked to the case. If the case has no linked alerts the response is an empty chain — never a 500.' operationId: get_attack_chain_api_v1_cases__case_id__attack_chain_get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string format: uuid title: Case Id - name: window in: query required: false schema: enum: - 1h - 6h - 24h - 72h - 7d - 30d type: string default: 24h title: Window responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Get Attack Chain Api V1 Cases Case Id Attack Chain Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/connectors/catalog: get: tags: - connectors summary: List Catalog description: 'Return the catalog of available connectors with their schemas. The wizard calls this once on open to populate the connector picker and to render the schema-driven configuration form. It is tenant-agnostic — every tenant in this build sees the same catalog — but still gated behind authentication.' operationId: list_catalog_api_v1_connectors_catalog_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response List Catalog Api V1 Connectors Catalog Get security: - HTTPBearer: [] /api/v1/connectors/health: get: tags: - connectors summary: Connector Health Summary description: 'Aggregate per-tenant connector health for the dashboard tile. Single SELECT scan; we deliberately do *not* group in SQL because Postgres'' filter expressions over a few hundred rows are negligible compared to the round-trip, and the resulting Python is a lot easier to read than five COUNT(*) FILTER clauses.' operationId: connector_health_summary_api_v1_connectors_health_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ConnectorHealthSummary' security: - HTTPBearer: [] /api/v1/connectors/test: post: tags: - connectors summary: Test Connection description: 'Wizard-side "Test connection" before saving. The wizard sends plaintext credentials directly here. We validate the connector type against the catalog, then forward the payload to the connectors microservice''s stateless test endpoint. **Nothing is persisted** — these credentials never touch Postgres or the vault.' operationId: test_connection_api_v1_connectors_test_post requestBody: content: application/json: schema: $ref: '#/components/schemas/TestConnectionRequest' required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Test Connection Api V1 Connectors Test Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/connectors: get: tags: - connectors summary: List Connectors description: List all connector instances for the caller's tenant. operationId: list_connectors_api_v1_connectors_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/ConnectorResponse' type: array title: Response List Connectors Api V1 Connectors Get security: - HTTPBearer: [] post: tags: - connectors summary: Create Connector description: 'Create a new connector instance. ``connector_type`` is validated against the live catalog. ``category`` defaults to the catalog entry''s category if the caller doesn''t override it — that''s the common case and keeps the wizard payload small. ``auth_config`` is encrypted with the credential vault before it touches Postgres.' operationId: create_connector_api_v1_connectors_post requestBody: content: application/json: schema: $ref: '#/components/schemas/CreateConnectorRequest' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ConnectorResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/connectors/{connector_id}: get: tags: - connectors summary: Get Connector description: Get a connector instance by ID, scoped to the caller's tenant. operationId: get_connector_api_v1_connectors__connector_id__get security: - HTTPBearer: [] 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/ConnectorResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' patch: tags: - connectors summary: Update Connector description: 'Update a connector''s configuration or state. The PATCH semantics here are deliberately conservative: only fields the caller actually supplies are touched. Notably, sending an empty ``auth_config`` dict will overwrite all secrets — the wizard must therefore omit the field entirely when the operator hasn''t re-typed credentials.' operationId: update_connector_api_v1_connectors__connector_id__patch security: - HTTPBearer: [] parameters: - name: connector_id in: path required: true schema: type: string format: uuid title: Connector Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateConnectorRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ConnectorResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - connectors summary: Delete Connector description: Delete a connector instance. operationId: delete_connector_api_v1_connectors__connector_id__delete security: - HTTPBearer: [] parameters: - name: connector_id in: path required: true schema: type: string format: uuid title: Connector Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/connectors/{connector_id}/capabilities: put: tags: - connectors summary: Update Connector Capabilities description: "Downscope which capabilities the agent may invoke against this\ \ instance.\n\nThe contract:\n\n* ``allowed_capabilities = None`` clears the\ \ downscope. The agent reverts\n to the connector class's full ``capabilities()``\ \ declaration.\n* ``allowed_capabilities = []`` is a *legitimate* operator\ \ action: it\n pins the agent to zero capabilities for this instance. Polling\ \ and\n data ingestion are unaffected — only agent-initiated actions are\n\ \ gated by this column.\n* Any string in the list MUST appear in the connector\ \ class's declared\n capabilities. We validate against the live catalog so\ \ a tampered\n request can't widen the agent's reach beyond what the connector\ \ code\n actually implements.\n\nThe narrowing happens at the read path\n\ (``BaseConnector.effective_capabilities()``), not here, so the column\nis\ \ purely a per-instance allow-list — flipping it back to ``None``\ninstantly\ \ restores the class default with no migration." operationId: update_connector_capabilities_api_v1_connectors__connector_id__capabilities_put security: - HTTPBearer: [] parameters: - name: connector_id in: path required: true schema: type: string format: uuid title: Connector Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateCapabilitiesRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ConnectorResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/connectors/{connector_id}/test: post: tags: - connectors summary: Test Existing Connector description: "Run a live ``test_connection`` against a saved connector instance.\n\ \nFlow:\n\n1. Fetch the row, scoped to the caller's tenant.\n2. Decrypt ``auth_config``\ \ via the vault.\n3. Forward to the connectors microservice with both the\ \ decrypted\n credentials and the non-secret ``connector_config``.\n4. Update\ \ the row's ``health_status`` / ``last_health_check`` based on\n the verdict\ \ so the dashboard reflects reality." operationId: test_existing_connector_api_v1_connectors__connector_id__test_post security: - HTTPBearer: [] 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: type: object additionalProperties: true title: Response Test Existing Connector Api V1 Connectors Connector Id Test Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/connectors/{connector_id}/last_event_at: get: tags: - connectors summary: Get Last Event At description: 'Return event-arrival watermark for the onboarding verify screen. Polled by the wizard''s "verify data flowing" panel every few seconds after a connector is saved. Returns 200 with ``data_flowing=false`` rather than a 404 so the polling loop has stable shape until the first event lands. The ``data_flowing`` boolean is computed server side from a fixed window (see ``_DATA_FLOWING_WINDOW_SECONDS``) so every client agrees on the rule.' operationId: get_last_event_at_api_v1_connectors__connector_id__last_event_at_get security: - HTTPBearer: [] 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/LastEventResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/connectors/troubleshoot: post: tags: - connectors summary: Troubleshoot Connection description: 'Return structured fix suggestions for a failed connector test. The wizard calls this when ``test_connection`` returns ``success=false``. Auth-config keys (not values) are forwarded so future LLM-backed versions can hint about which field to revisit, while keeping the request safe to log.' operationId: troubleshoot_connection_api_v1_connectors_troubleshoot_post requestBody: content: application/json: schema: $ref: '#/components/schemas/TroubleshootRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TroubleshootResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/connectors/{connector_id}/push/refresh: post: tags: - connectors summary: Refresh Ingest Token description: 'Generate (or rotate) the per-connector push-ingest token. Operators click "Reveal push URL" in the wizard, which calls this and renders the resulting curl example. Re-calling rotates the token and invalidates the old one — the typical use case is "I think this URL leaked into a Slack message, give me a new one".' operationId: refresh_ingest_token_api_v1_connectors__connector_id__push_refresh_post security: - HTTPBearer: [] 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/IngestTokenResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/oauth/app/{connector_type}: get: tags: - oauth summary: Get Oauth App description: Return the tenant's registered OAuth app (without the secret). operationId: get_oauth_app_api_v1_oauth_app__connector_type__get security: - HTTPBearer: [] parameters: - name: connector_type in: path required: true schema: type: string title: Connector Type responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/OAuthAppView' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' put: tags: - oauth summary: Upsert Oauth App description: Register or rotate the tenant's OAuth client for this connector class. operationId: upsert_oauth_app_api_v1_oauth_app__connector_type__put security: - HTTPBearer: [] parameters: - name: connector_type in: path required: true schema: type: string title: Connector Type requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/OAuthAppRegistration' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/OAuthAppView' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - oauth summary: Delete Oauth App description: Unregister the tenant's OAuth app for this connector class. operationId: delete_oauth_app_api_v1_oauth_app__connector_type__delete security: - HTTPBearer: [] parameters: - name: connector_type in: path required: true schema: type: string title: Connector Type responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/oauth/start: get: tags: - oauth summary: Oauth Start description: Mint a state nonce and bounce the operator to the provider's authorize URL. operationId: oauth_start_api_v1_oauth_start_get security: - HTTPBearer: [] parameters: - name: connector_type in: query required: true schema: type: string description: Connector class to OAuth into title: Connector Type description: Connector class to OAuth into - name: connector_id in: query required: false schema: anyOf: - type: string format: uuid - type: 'null' description: Re-auth target; omit to mint a new connector title: Connector Id description: Re-auth target; omit to mint a new connector - name: return_to in: query required: false schema: anyOf: - type: string - type: 'null' description: Where to land after callback (defaults to /onboarding) title: Return To description: Where to land after callback (defaults to /onboarding) - name: name in: query required: false schema: anyOf: - type: string - type: 'null' description: Operator-supplied display name for new connectors title: Name description: Operator-supplied display name for new connectors - name: extras in: query required: false schema: anyOf: - type: string - type: 'null' description: JSON dict of provider-specific extras (e.g. organization for GitHub) title: Extras description: JSON dict of provider-specific extras (e.g. organization for GitHub) - name: response_mode in: query required: false schema: type: string pattern: ^(redirect|json)$ default: redirect title: Response Mode responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/oauth/callback: get: tags: - oauth summary: Oauth Callback description: 'Handle the provider''s redirect after the operator consents. No auth dependency: this endpoint is invoked by the *browser* after a third-party redirect, not by an authenticated console session. The CSRF defence is the ``state`` nonce, which we issued ourselves at /oauth/start under an authenticated session.' operationId: oauth_callback_api_v1_oauth_callback_get parameters: - name: code in: query required: false schema: anyOf: - type: string - type: 'null' title: Code - name: state in: query required: false schema: anyOf: - type: string - type: 'null' title: State - name: error in: query required: false schema: anyOf: - type: string - type: 'null' title: Error - name: error_description in: query required: false schema: anyOf: - type: string - type: 'null' title: Error Description responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/onboarding/status: get: tags: - onboarding summary: Onboarding Status description: 'What this tenant has set up, derived from its data. Derived rather than stored. A `tenant.onboarded` boolean drifts the moment somebody connects a source through the API, deletes their last connector, or restores a backup — and a wizard that insists you are finished when your estate is empty is worse than no wizard.' operationId: onboarding_status_api_v1_onboarding_status_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/OnboardingStatus' security: - HTTPBearer: [] /api/v1/onboarding/sample-data: post: tags: - onboarding summary: Load Sample Data description: 'Push five labelled scenarios through the real pipeline. Refuses on a tenant that already has real alerts. Sample rows in a live queue are indistinguishable from real ones at a glance, and an analyst who dismisses a genuine alert because they assumed it was sample data is a worse outcome than an evaluator having to use a second tenant.' operationId: load_sample_data_api_v1_onboarding_sample_data_post responses: '202': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SampleDataResponse' security: - HTTPBearer: [] /api/v1/tenants/me/identity: get: tags: - tenants summary: Get My Tenant Identity description: 'Get minimal tenant identity for the current user. Returns only `id`, `name`, `mssp_role`, and `parent_tenant_id`. This is safe for **any** authenticated user (analyst, viewer, responder, etc.) because it does not expose plan, settings, or limits. Used by the SOC console TopBar to render the tenant switcher pill and role badge.' operationId: get_my_tenant_identity_api_v1_tenants_me_identity_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TenantHeaderResponse' security: - HTTPBearer: [] /api/v1/tenants/me: get: tags: - tenants summary: Get My Tenant description: Get the current user's tenant details (full config — requires settings:read). operationId: get_my_tenant_api_v1_tenants_me_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TenantResponse' security: - HTTPBearer: [] /api/v1/tenants/me/settings: patch: tags: - tenants summary: Update Tenant Settings description: Update tenant settings. operationId: update_tenant_settings_api_v1_tenants_me_settings_patch requestBody: content: application/json: schema: $ref: '#/components/schemas/UpdateTenantSettingsRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TenantResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/tenants/me/delete: post: tags: - tenants summary: Delete My Tenant description: 'Erase this tenant from Postgres, ClickHouse, Neo4j, Qdrant and Redis. Defaults to a dry run, which counts what would be removed per store without deleting. A live run requires ``confirm_tenant_id`` to match, and reports per-store results: if any store fails, the Postgres transaction is rolled back so the tenant record still names whatever data survived elsewhere, and ``complete`` is false.' operationId: delete_my_tenant_api_v1_tenants_me_delete_post requestBody: content: application/json: schema: $ref: '#/components/schemas/TenantDeletionRequest' required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Delete My Tenant Api V1 Tenants Me Delete Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/tenants/me/users: get: tags: - tenants summary: List Users description: List all users in the current tenant. operationId: list_users_api_v1_tenants_me_users_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/UserResponse' type: array title: Response List Users Api V1 Tenants Me Users Get security: - HTTPBearer: [] post: tags: - tenants summary: Create User description: 'Create a new user in the current tenant. The role must be one the caller could hold itself. `platform_admin` and `admin` cannot be assigned through the API at all.' operationId: create_user_api_v1_tenants_me_users_post requestBody: content: application/json: schema: $ref: '#/components/schemas/CreateUserRequest' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/UserResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/tenants/me/users/{user_id}: patch: tags: - tenants summary: Update User description: 'Update a user. A role change is bounded the same way creation is, and a principal already holding `platform_admin` or `admin` cannot be re-roled here.' operationId: update_user_api_v1_tenants_me_users__user_id__patch security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string format: uuid title: User Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateUserRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/UserResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/usage: get: tags: - usage summary: Get Usage description: Daily usage for the caller's tenant, defaulting to the last 30 days. operationId: get_usage_api_v1_usage_get security: - HTTPBearer: [] parameters: - name: start in: query required: false schema: anyOf: - type: string - type: 'null' title: Start - name: end in: query required: false schema: anyOf: - type: string - type: 'null' title: End responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Get Usage Api V1 Usage Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/usage/reconciliation: get: tags: - usage summary: Get Reconciliation description: 'Compare the daily sum against one query over the whole window. Exposed rather than kept in a test so an operator disputing an invoice can run the same check the test runs, against their own rows.' operationId: get_reconciliation_api_v1_usage_reconciliation_get security: - HTTPBearer: [] parameters: - name: start in: query required: false schema: anyOf: - type: string - type: 'null' title: Start - name: end in: query required: false schema: anyOf: - type: string - type: 'null' title: End responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Get Reconciliation Api V1 Usage Reconciliation Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/usage/export.csv: get: tags: - usage summary: Export Month description: A month of usage as CSV, labelled with the organisation it belongs to. operationId: export_month_api_v1_usage_export_csv_get security: - HTTPBearer: [] parameters: - name: month in: query required: false schema: anyOf: - type: string - type: 'null' description: YYYY-MM; defaults to the current month title: Month description: YYYY-MM; defaults to the current month responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/usage/organization/export.csv: get: tags: - usage summary: Export Organization Month description: 'A month of usage for every tenant the caller''s organisation manages. The per-tenant export names an organisation in its header and covers one tenant, which left a provider with forty customers making forty requests and adding up the columns by hand. The tenant list is whatever `resolve_portfolio_scope` returns for this principal, and nothing here widens it. A member scoped to three of a forty-tenant portfolio exports three, and a principal who belongs to no organisation is refused rather than silently handed their own tenant — an export that quietly changes scope is worse than one that fails.' operationId: export_organization_month_api_v1_usage_organization_export_csv_get security: - HTTPBearer: [] parameters: - name: month in: query required: false schema: anyOf: - type: string - type: 'null' description: YYYY-MM; defaults to the current month title: Month description: YYYY-MM; defaults to the current month responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/usage/enforcement: get: tags: - usage summary: Get Enforcement description: The limit keys this tenant enforces. Absence of a row means none. operationId: get_enforcement_api_v1_usage_enforcement_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/EnforcementPolicy' security: - HTTPBearer: [] put: tags: - usage summary: Put Enforcement description: 'Turn enforcement on or off, per limit key, for the caller''s tenant. The tenant is the caller''s and never a parameter: a route that let one authenticated user switch enforcement on for somebody else''s tenant would be a denial of service against them, delivered as a setting. Every name is checked against `LIMIT_KEYS` before it is stored. A typo would otherwise be accepted, match no key, enforce nothing, and leave an operator reading a page that says a limit is enforced while it is not — which is the quiet direction and the one worth refusing loudly.' operationId: put_enforcement_api_v1_usage_enforcement_put requestBody: content: application/json: schema: $ref: '#/components/schemas/EnforcementPolicy' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/EnforcementPolicy' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/rules: get: tags: - detection_rules summary: List Rules description: 'List the tenant''s effective detection rules. Three sources, merged: the compiled corpus the fusion engine loads, the tenant''s own rules, and rules from MSSP-assigned packs (with excluded overrides removed). Before #1273 only the last two were read, and since nothing in production has ever written a built-in row, a default install answered with an empty list while the engine ran 2,609 rules. ``X-Total-Count`` carries the number matching the filters, which is not the number returned: the response stays a bare array so existing callers are unaffected, and the page bound exists because serialising the whole library is several megabytes.' operationId: list_rules_api_v1_rules_get security: - HTTPBearer: [] parameters: - name: category in: query required: false schema: anyOf: - type: string - type: 'null' title: Category - name: rule_language in: query required: false schema: anyOf: - type: string - type: 'null' title: Rule Language - name: include_builtin in: query required: false schema: type: boolean default: true title: Include Builtin - name: include_packs in: query required: false schema: type: boolean default: true title: Include Packs - name: search in: query required: false schema: anyOf: - type: string maxLength: 200 - type: 'null' description: Case-insensitive match on name, id, category, tags and techniques. title: Search description: Case-insensitive match on name, id, category, tags and techniques. - name: severity in: query required: false schema: anyOf: - type: string - type: 'null' title: Severity - name: mitre in: query required: false schema: anyOf: - type: string - type: 'null' description: MITRE ATT&CK technique id, e.g. T1059.001 title: Mitre description: MITRE ATT&CK technique id, e.g. T1059.001 - name: limit in: query required: false schema: type: integer maximum: 2000 minimum: 1 description: Maximum rules returned. The full library is several MB. default: 200 title: Limit description: Maximum rules returned. The full library is several MB. - 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/DetectionRuleResponse' title: Response List Rules Api V1 Rules Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - detection_rules summary: Create Rule description: Create a new detection rule. operationId: create_rule_api_v1_rules_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateRuleRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/DetectionRuleResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/rules/{rule_id}: get: tags: - detection_rules summary: Get Rule description: Get a detection rule by ID, stored row first and compiled corpus second. operationId: get_rule_api_v1_rules__rule_id__get security: - HTTPBearer: [] parameters: - name: rule_id in: path required: true schema: type: string format: uuid title: Rule Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/DetectionRuleResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' patch: tags: - detection_rules summary: Update Rule description: 'Update a tenant-owned rule, or record a tuning decision on a built-in. A built-in materialises its tenant row on first write, under the rule''s own id and carrying ``provenance.source_id`` — the key fusion''s tenant overlay joins on — so a status change here reaches the engine within one overlay reload. ``rule_body`` is refused on a built-in: the engine loads the compiled artefact, so a stored edit would be displayed as applied and never run.' operationId: update_rule_api_v1_rules__rule_id__patch security: - HTTPBearer: [] parameters: - name: rule_id in: path required: true schema: type: string format: uuid title: Rule Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/app__api__v1__endpoints__detection_rules__UpdateRuleRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/DetectionRuleResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - detection_rules summary: Delete Rule description: Delete a tenant-owned detection rule. operationId: delete_rule_api_v1_rules__rule_id__delete security: - HTTPBearer: [] parameters: - name: rule_id in: path required: true schema: type: string format: uuid title: Rule Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/rules/{rule_id}/execute: post: tags: - detection_rules summary: Execute a detection rule against provided events description: 'Execute a single detection rule against a set of events and return matches. Useful for testing rules in the detection IDE before enabling them.' operationId: execute_detection_rule_api_v1_rules__rule_id__execute_post security: - HTTPBearer: [] parameters: - name: rule_id in: path required: true schema: type: string format: uuid title: Rule Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ExecuteRuleRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ExecuteRuleResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/rules/hunt: post: tags: - detection_rules summary: 'Threat hunt: run detection rules against a set of events' description: "Run multiple detection rules against provided events (threat hunting).\n\ \nThe effective rule set is resolved through\n:func:`app.services.mssp_rule_resolver.resolve_effective_rules`,\ \ which layers:\n\n 1. Tenant-owned rules\n 2. Platform-wide built-in rules\n\ \ 3. Rules sourced from MSSP-assigned rule packs\n 4. Per-tenant exclude\ \ / customize overrides\n\nIf ``rule_ids`` is omitted, all active rules in\ \ the resolved set are used." operationId: hunt_api_v1_rules_hunt_post requestBody: content: application/json: schema: $ref: '#/components/schemas/HuntRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/app__api__v1__endpoints__detection_rules__HuntResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/rules/{rule_id}/backtest: post: tags: - detection_rules summary: Backtest a detection rule against historical lake events description: 'Run a rule over the last N days of REAL events in the ClickHouse lake and report exactly how many would have fired (Wave 2). Tenant-scoped via ``lake_sql.rewrite_for_tenant``. Read-only.' operationId: backtest_detection_rule_api_v1_rules__rule_id__backtest_post security: - HTTPBearer: [] parameters: - name: rule_id in: path required: true schema: type: string format: uuid title: Rule Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BacktestRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/BacktestResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/detection/rules: get: tags: - detection_rules summary: List Rules Compat description: 'The tenant''s effective rule library: the compiled corpus plus its tuning. This used to read ``detection_rules`` alone and therefore answered with an empty list on every install, while fusion loaded and fired 2,609 rules (#1273). The filters and the page bound are not cosmetic: the whole library serialised is several megabytes, so an unbounded list would have traded an empty page for an unusable one.' operationId: list_rules_compat_api_v1_detection_rules_get security: - HTTPBearer: [] parameters: - name: search in: query required: false schema: anyOf: - type: string maxLength: 200 - type: 'null' title: Search - name: severity in: query required: false schema: anyOf: - type: string - type: 'null' title: Severity - name: category in: query required: false schema: anyOf: - type: string - type: 'null' title: Category - name: mitre in: query required: false schema: anyOf: - type: string - type: 'null' description: MITRE ATT&CK technique id, e.g. T1059.001 title: Mitre description: MITRE ATT&CK technique id, e.g. T1059.001 - name: enabled in: query required: false schema: anyOf: - type: boolean - type: 'null' title: Enabled - name: source in: query required: false schema: enum: - all - builtin - custom type: string default: all title: Source - name: limit in: query required: false schema: type: integer maximum: 1000 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/ListResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - detection_rules summary: Create Rule Compat description: Create a tenant-owned detection rule using the frontend shape. operationId: create_rule_compat_api_v1_detection_rules_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateBody' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/FrontendDetectionRule' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/detection/rules/{rule_id}: get: tags: - detection_rules summary: Get Rule Compat description: 'Fetch a single rule by ID in the frontend shape. Resolves a stored row first and the compiled catalogue second, so a built-in''s detail page opens without a row having to exist for it. The read never creates one: a GET that materialised would put 2,609 rows per tenant in a table meant to hold decisions.' operationId: get_rule_compat_api_v1_detection_rules__rule_id__get security: - HTTPBearer: [] parameters: - name: rule_id in: path required: true schema: type: string format: uuid title: Rule Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/FrontendDetectionRule' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' patch: tags: - detection_rules summary: Update Rule Compat description: 'Update a tenant-owned rule, or record a tuning decision on a built-in. A built-in has no row until this point. The first write creates one under the rule''s own id, carrying ``provenance.source_id`` — the key fusion''s tenant overlay joins on — so disabling a rule in the console stops the engine firing it rather than setting a flag nothing reads. Before #1273 this route answered 404 for every rule the engine was actually running. The compiled logic itself is not editable here: ``rule_body``, ``language`` and ``mitre`` are refused on a built-in rather than silently stored, because the engine loads the artefact and would ignore the edit while the console displayed it as applied.' operationId: update_rule_compat_api_v1_detection_rules__rule_id__patch security: - HTTPBearer: [] parameters: - name: rule_id in: path required: true schema: type: string format: uuid title: Rule Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateBody' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/FrontendDetectionRule' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - detection_rules summary: Delete Rule Compat description: Delete a tenant-owned rule. operationId: delete_rule_compat_api_v1_detection_rules__rule_id__delete security: - HTTPBearer: [] parameters: - name: rule_id in: path required: true schema: type: string format: uuid title: Rule Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/detection/test: post: tags: - detection_rules summary: Test Rule Compat description: 'Run an ad-hoc rule body against an optional sample event blob. The detection IDE in the analyst console hits this with the rule body the user is actively editing, which has not been persisted yet, so the rule never needs to live in the database.' operationId: test_rule_compat_api_v1_detection_test_post requestBody: content: application/json: schema: $ref: '#/components/schemas/TestBody' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TestResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/detection/coverage: get: tags: - detection_rules summary: Get Detection Coverage description: 'Rule-centric MITRE ATT&CK coverage for the current tenant. Distinct from ``/api/v1/graph/mitre/coverage`` which derives coverage from *alerts* — this one is what an analyst opens before a tuning sprint to answer "which techniques is my rule library blind to?". Reads the whole effective library, built-ins included. It previously read the ``detection_rules`` table alone and therefore drew an empty heatmap over a product shipping ATT&CK mappings for 2,609 loaded rules — the strongest possible version of the "blind to everything" answer, and wrong.' operationId: get_detection_coverage_api_v1_detection_coverage_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CoverageResponse' security: - HTTPBearer: [] /api/v1/detection/rules/bulk-toggle: post: tags: - detection_rules summary: Bulk Toggle Rules description: 'Enable or disable many rules in one round-trip, built-ins included. Built-ins used to be "silently skipped", which on a default install meant every rule in the library was skipped: there were no other rules. A built-in now materialises a tenant tuning row on first toggle, exactly as the single-rule PATCH does, so selecting a noisy category and turning it off reaches the engine. Ids that match nothing at all are still reported in ``skipped``, because an operator who selected forty rules and had one quietly dropped would have no way to find out which.' operationId: bulk_toggle_rules_api_v1_detection_rules_bulk_toggle_post requestBody: content: application/json: schema: $ref: '#/components/schemas/BulkToggleBody' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/BulkToggleResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/detection/drift: get: tags: - detection_rules summary: Get Detection Drift description: 'Detection drift inbox — rules that need an analyst''s attention. Surfaces rules with elevated FP rate, low confidence, or stale (no recent triggers despite being enabled). The UI renders this as a tab on the Detections page so analysts have a single queue to work instead of paginating the full library hunting for noise. Built-ins are counted in ``summary.unscored`` rather than judged: none of the three heuristics has an input for them.' operationId: get_detection_drift_api_v1_detection_drift_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/DriftResponse' security: - HTTPBearer: [] /api/v1/detection/confidence: get: tags: - detection_rules summary: Get Detection Confidence description: 'Rule-confidence trend panel for the WS-B3 Detections UI. Returns a 4-bucket histogram of rule confidence, the per-tactic average, and the top/bottom rules so analysts can see at a glance where the rule library is brittle and which tactics need the most tuning love. No history table is needed — this view derives its trend signal from the current confidence/FP-rate columns set by the rule engine and operator review on every match. Rules with no recorded confidence are reported as ``summary.unscored`` and left out of every average, so the histogram describes the rules an operator has actually scored rather than being flattened by a library of placeholders.' operationId: get_detection_confidence_api_v1_detection_confidence_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ConfidenceResponse' security: - HTTPBearer: [] /api/v1/detection-proposals: get: tags: - detection_rules - dac summary: List Proposals description: List detection rule proposals visible to the caller. operationId: list_proposals_api_v1_detection_proposals_get security: - HTTPBearer: [] parameters: - name: status in: query required: false schema: anyOf: - type: string - type: 'null' title: Status - name: limit in: query required: false schema: type: integer maximum: 500 minimum: 1 default: 100 title: Limit responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/ProposalResponse' title: Response List Proposals Api V1 Detection Proposals Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - detection_rules - dac summary: Create Proposal description: Open a new detection proposal in `proposed` state. operationId: create_proposal_api_v1_detection_proposals_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateProposalRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ProposalResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/detection-proposals/baselines: get: tags: - detection_rules - dac summary: List Baselines description: List eval baselines (most recent active baseline per suite is what we gate on). operationId: list_baselines_api_v1_detection_proposals_baselines_get security: - HTTPBearer: [] parameters: - name: suite in: query required: false schema: anyOf: - type: string - type: 'null' title: Suite responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/BaselineResponse' title: Response List Baselines Api V1 Detection Proposals Baselines Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - detection_rules - dac summary: Record Baseline description: Record a new eval baseline and deactivate older entries for the same suite. operationId: record_baseline_api_v1_detection_proposals_baselines_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateBaselineRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/BaselineResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/detection-proposals/{proposal_id}: get: tags: - detection_rules - dac summary: Get Proposal description: Get a proposal by id. operationId: get_proposal_api_v1_detection_proposals__proposal_id__get security: - HTTPBearer: [] parameters: - name: proposal_id in: path required: true schema: type: string format: uuid title: Proposal Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ProposalResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/detection-proposals/{proposal_id}/comment: post: tags: - detection_rules - dac summary: Comment On Proposal description: Append a review comment and move the proposal into `in_review`. operationId: comment_on_proposal_api_v1_detection_proposals__proposal_id__comment_post security: - HTTPBearer: [] parameters: - name: proposal_id in: path required: true schema: type: string format: uuid title: Proposal Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ReviewCommentRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ProposalResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/detection-proposals/run-eval: post: tags: - detection_rules - dac summary: Run the offline eval harness (`scripts/run_evals.py`) and return the JSON report description: 'Execute the offline eval harness and return its JSON output. This is the "eval-harness regression on save" hook for the in-app rule editor. The harness is deterministic and offline, but takes 5–15s, so the editor surfaces it as an explicit "Propose for review" action rather than firing on every keystroke.' operationId: run_eval_harness_api_v1_detection_proposals_run_eval_post requestBody: content: application/json: schema: $ref: '#/components/schemas/RunEvalRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RunEvalResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/detection-proposals/{proposal_id}/evaluate-rule: post: tags: - detection_rules - dac summary: Evaluate Rule description: 'Evaluate the candidate rule body against its own fixtures (Phase 4). Runs the proposed ``rule_language`` + ``rule_body`` through the runtime engine against the supplied fixtures and stores the verdict under ``eval_result["candidate_rule"]``. This is the gate that de-circularises promotion: a rule that misses its positives (blind) or fires on its negatives (noisy) cannot be approved, regardless of the global benchmark.' operationId: evaluate_rule_api_v1_detection_proposals__proposal_id__evaluate_rule_post security: - HTTPBearer: [] parameters: - name: proposal_id in: path required: true schema: type: string format: uuid title: Proposal Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/EvaluateRuleRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ProposalResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/detection-proposals/{proposal_id}/backtest: post: tags: - detection_rules - dac summary: Backtest Proposal description: 'Run the candidate rule over the last N days of REAL lake events and attach the result to ``eval_result["backtest"]`` (Wave 2, W2.2). This is a third, honest signal alongside the fixture gate + MITRE baseline: a rule that would have fired on a large fraction of historical events is noisy. When ``AISOC_BACKTEST_MAX_HIT_RATE`` is set, ``/decide`` blocks approval above that hit-rate (opt-in — informational by default).' operationId: backtest_proposal_api_v1_detection_proposals__proposal_id__backtest_post security: - HTTPBearer: [] parameters: - name: proposal_id in: path required: true schema: type: string format: uuid title: Proposal Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BacktestProposalRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ProposalResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/detection-proposals/{proposal_id}/eval: post: tags: - detection_rules - dac summary: Attach Eval Result description: 'Attach the run_evals.py output and compute the gate verdict. Reads the active MITRE accuracy baseline for the tenant (falling back to the platform-wide baseline) and stores ``eval_result.passed=True`` only when MITRE accuracy regression is < ``max_regression_pp``.' operationId: attach_eval_result_api_v1_detection_proposals__proposal_id__eval_post security: - HTTPBearer: [] parameters: - name: proposal_id in: path required: true schema: type: string format: uuid title: Proposal Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/EvalAttachRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ProposalResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/detection-proposals/{proposal_id}/decide: post: tags: - detection_rules - dac summary: Decide Proposal description: Approve or reject a proposal. Approving requires the eval gate to have passed. operationId: decide_proposal_api_v1_detection_proposals__proposal_id__decide_post security: - HTTPBearer: [] parameters: - name: proposal_id in: path required: true schema: type: string format: uuid title: Proposal Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DecisionRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ProposalResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/detection-proposals/{proposal_id}/promote: post: tags: - detection_rules - dac summary: Materialise an approved proposal into the detection_rules table description: 'Promote an approved proposal: create or update the linked detection rule.' operationId: promote_proposal_api_v1_detection_proposals__proposal_id__promote_post security: - HTTPBearer: [] parameters: - name: proposal_id in: path required: true schema: type: string format: uuid title: Proposal Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ProposalResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/detection/tuning/auto-suggest: post: tags: - detection summary: Auto Suggest Tuning description: 'Derive human-approval-gated tuning suggestions from disposition history. Wave 1 wiring: connects the previously-orphaned ``suggest_tuning`` engine to real per-rule disposition history and, optionally, opens DRAFT detection proposals (source ``auto-tuner``) into the governed lifecycle. Nothing is auto-applied — an engineer reviews + eval-gates before promotion.' operationId: auto_suggest_tuning_api_v1_detection_tuning_auto_suggest_post security: - HTTPBearer: [] parameters: - name: window_days in: query required: false schema: type: integer maximum: 180 minimum: 1 default: 30 title: Window Days - name: create_proposals in: query required: false schema: type: boolean default: false title: Create Proposals responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AutoSuggestResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/detection/tuning: get: tags: - detection summary: Get Tuning Workbench description: 'Project every rule the tenant can tune into a workbench feed. The summary block is computed across the *entire classified population* (not the current page), so the header tiles stay stable as analysts page. Dismissed rules are excluded by default — pass ``include_dismissed=true`` when auditing what''s been hidden.' operationId: get_tuning_workbench_api_v1_detection_tuning_get security: - HTTPBearer: [] parameters: - name: severity in: query required: false schema: anyOf: - type: string - type: 'null' description: Filter by rule severity (info/low/medium/high/critical). title: Severity description: Filter by rule severity (info/low/medium/high/critical). - name: suggestion in: query required: false schema: anyOf: - enum: - disable - add_suppression - raise_threshold - tune_confidence - review_stale - healthy type: string - type: 'null' description: Filter to a single suggestion lane. title: Suggestion description: Filter to a single suggestion lane. - name: search in: query required: false schema: anyOf: - type: string maxLength: 200 - type: 'null' description: Substring match on name/description/category. title: Search description: Substring match on name/description/category. - name: enabled_only in: query required: false schema: type: boolean description: Hide rules whose status != 'active'. default: false title: Enabled Only description: Hide rules whose status != 'active'. - name: include_dismissed in: query required: false schema: type: boolean description: Show rules previously dismissed from the workbench. default: false title: Include Dismissed description: Show rules previously dismissed from the workbench. - 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: 50 title: Page Size responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TuningResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/detection/tuning/summary: get: tags: - detection summary: Get Tuning Summary description: 'Cheap summary-only endpoint for sidebar badges and the /console dashboard tile.' operationId: get_tuning_summary_api_v1_detection_tuning_summary_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TuningSummary' security: - HTTPBearer: [] /api/v1/detection/tuning/{rule_id}/apply: post: tags: - detection summary: Apply Tuning Endpoint description: 'Mechanically apply a tuning suggestion to a single rule. Supports ``raise_threshold``, ``add_suppression``, ``disable``, and ``acknowledge`` (no-op + audit). Every mutation bumps ``DetectionRule.version`` and writes a ``detection.tuning.apply`` audit log entry. Returns the re-projected entry so the UI can refresh in place.' operationId: apply_tuning_endpoint_api_v1_detection_tuning__rule_id__apply_post security: - HTTPBearer: [] parameters: - name: rule_id in: path required: true schema: type: string format: uuid title: Rule Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ApplyTuningRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TuningEntry' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/detection/tuning/{rule_id}/dismiss: post: tags: - detection summary: Dismiss Tuning Endpoint description: Hide a rule from the default workbench view without changing semantics. operationId: dismiss_tuning_endpoint_api_v1_detection_tuning__rule_id__dismiss_post security: - HTTPBearer: [] parameters: - name: rule_id in: path required: true schema: type: string format: uuid title: Rule Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DismissTuningRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TuningEntry' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/detection/tuning/{rule_id}/auto_tune: post: tags: - detection summary: Set Auto Tune Endpoint description: 'Flip the per-rule ``auto_tune`` opt-in flag. The flag is stored under ``suppression_config.auto_tune`` and is read by future automated tuners — flipping it does *not* trigger any immediate rule mutation.' operationId: set_auto_tune_endpoint_api_v1_detection_tuning__rule_id__auto_tune_post security: - HTTPBearer: [] parameters: - name: rule_id in: path required: true schema: type: string format: uuid title: Rule Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AutoTuneRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TuningEntry' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/federated/backends: get: tags: - federated-search summary: List Federated Backends description: 'List the connector instances that ``POST /search`` would fan out to. Useful for the wizard preview ("you''ll search 1 Splunk + 1 Elastic") and for the SDK to build sensible default ``connector_ids`` lists.' operationId: list_federated_backends_api_v1_federated_backends_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/FederatedBackendsResponse' security: - HTTPBearer: [] /api/v1/federated/search: post: tags: - federated-search summary: Federated Search description: 'Run a unified query across every federated-capable connector. Per-backend errors do not fail the call. The response always carries a ``sources[]`` array describing the verdict for each backend that was attempted; the merged ``rows[]`` only contains rows from the ``ok`` sources.' operationId: federated_search_api_v1_federated_search_post requestBody: content: application/json: schema: $ref: '#/components/schemas/FederatedSearchRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/FederatedSearchResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/agent-tools/backends: get: tags: - agent-tools summary: Which agent tools this tenant has a backend for description: 'Enumerate what can be asked, so nothing else is advertised. ``registry_reachable`` is part of the contract. A caller that reads an unreachable action registry as "this tenant has no vendor tools" will bind a smaller toolset and then investigate confidently without it, which is the worst of the three outcomes. Reported as unknown instead.' operationId: list_backends_api_v1_agent_tools_backends_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response List Backends Api V1 Agent Tools Backends Get security: - HTTPBearer: [] /api/v1/agent-tools/siem-search: post: tags: - agent-tools summary: Search every configured SIEM for one indicator description: 'Run one typed indicator search across the tenant''s SIEMs. The response separates three facts that an agent must not conflate: rows that matched, sources that answered, and sources that could not be searched. ``outcome`` is the single field a caller branches on.' operationId: search_siem_api_v1_agent_tools_siem_search_post requestBody: content: application/json: schema: $ref: '#/components/schemas/SiemSearchRequest' required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Search Siem Api V1 Agent Tools Siem Search Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/agent-tools/vendor-read: post: tags: - agent-tools summary: Run one read-only vendor verb description: 'Dispatch one read verb, under the capability contract. Refusals are 422 rather than a result with ``executed: false``, so a caller cannot mistake "this verb is not allowed here" for "the vendor had nothing". The one thing that is *not* a refusal is a vendor failure: that comes back 200 with ``executed: false`` and a reason, because the caller has to render it to a model as "could not check".' operationId: vendor_read_api_v1_agent_tools_vendor_read_post requestBody: content: application/json: schema: $ref: '#/components/schemas/VendorReadRequest' required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Vendor Read Api V1 Agent Tools Vendor Read Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/agent-tools/hunt-plan/execute: post: tags: - agent-tools summary: Run a hunting agent's structured plan against the event lake description: 'Compile a validated plan and run it, tenant-scoped. The tenant comes from the credential, never from the request, which is why there is no tenant field on the model above. An unreachable lake is reported as ``available: false`` with a reason rather than as zero rows: an agent that reads the second as the first concludes an estate is clean on evidence nobody gathered.' operationId: execute_hunt_plan_api_v1_agent_tools_hunt_plan_execute_post requestBody: content: application/json: schema: $ref: '#/components/schemas/HuntPlanExecuteRequest' required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Execute Hunt Plan Api V1 Agent Tools Hunt Plan Execute Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/graph: get: tags: - graph summary: Tenant-level entity graph for the Attack Graph console description: "The caller's own entity graph, bounded for one canvas render.\n\ \nScoping is the whole of this endpoint's security surface. Every node of\n\ every traversed path must satisfy the tenant predicate — not just the\nnode\ \ the walk started from — and a node with no ``tenant_id`` is not\nreadable,\ \ because an untagged node that *were* readable would bridge two\ntenants\ \ through any entity they happen to share. The narrow exemption is\nthe global\ \ MITRE labels, which belong to no tenant by design.\n\nTwo failure modes\ \ that must not look alike:\n\n``empty``\n 200 with no nodes. The tenant\ \ really has no graph yet — nothing has\n been ingested, or nothing ingested\ \ produced entities. The console\n renders its empty state.\n``unavailable``\n\ \ 503. The graph backend could not be reached, so we do not know what\n\ \ the tenant has. This deliberately does *not* degrade to an empty\n \ \ graph the way ``/graph/mitre-coverage`` does: an empty attack graph\n \ \ reads as \"no attack relationships exist in your estate\", which is a\n\ \ security claim, and making it on evidence we never retrieved is the\n\ \ failure this codebase keeps finding. The console's error state names\n\ \ the endpoint and the status, which is the honest answer." operationId: get_graph_overview_api_v1_graph_get security: - HTTPBearer: [] parameters: - name: depth in: query required: false schema: type: integer maximum: 6 minimum: 1 default: 3 title: Depth - name: entity in: query required: false schema: anyOf: - type: string maxLength: 256 - type: 'null' title: Entity responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/GraphOverviewResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/graph/attack-path/{case_id}: get: tags: - graph summary: Get attack path graph for a case description: 'Traverse the knowledge graph from a Case node to reconstruct the full attack path: Case → Alerts → Hosts/Users → IOCs → MITRE Techniques. Falls back to a relational reconstruction (Case → Alerts → Techniques) when the Neo4j graph backend is offline so the demo Attack Path UI keeps working without a graph database deployed.' operationId: get_attack_path_api_v1_graph_attack_path__case_id__get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string title: Case Id - name: max_depth in: query required: false schema: type: integer maximum: 10 minimum: 1 default: 6 title: Max Depth responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AttackPathResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/graph/blast-radius/{entity_type}/{entity_id}: get: tags: - graph summary: Compute blast radius from an entity description: 'Compute the blast radius starting from a Host, User, or IOC node. Returns all entities reachable within `hops` and a severity score.' operationId: get_blast_radius_api_v1_graph_blast_radius__entity_type___entity_id__get security: - HTTPBearer: [] parameters: - name: entity_type in: path required: true schema: type: string title: Entity Type - name: entity_id in: path required: true schema: type: string title: Entity Id - name: hops in: query required: false schema: type: integer maximum: 6 minimum: 1 default: 3 title: Hops responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/BlastRadiusResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/graph/incident-context/{alert_id}: get: tags: - graph summary: Traverse an alert into identity, asset, cloud, business and threat context description: 'Resolve one alert into the five dimensions an investigation needs. Each dimension runs as its own bounded, tenant-scoped traversal, and they run concurrently. A dimension that fails or times out names itself in ``errors`` while the rest still return: this is on the hot path of every escalated alert, so one slow leg must not take the bundle with it. Returns 200 with ``partial: true`` rather than an error status when some dimensions failed — the caller asked for context and got some.' operationId: incident_context_api_v1_graph_incident_context__alert_id__get security: - HTTPBearer: [] parameters: - name: alert_id in: path required: true schema: type: string title: Alert Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/IncidentContextResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/graph/investigate/tools: get: tags: - graph summary: List the investigation primitives and which are backed by data description: 'Enumerate the toolset, separating what can answer from what cannot. The split is part of the contract. A tool whose data class is not ingested reports that rather than returning an empty result, because an empty result reads as "I checked and found nothing" — which is how an investigation concludes benign on evidence it never had.' operationId: list_investigation_tools_api_v1_graph_investigate_tools_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response List Investigation Tools Api V1 Graph Investigate Tools Get security: - HTTPBearer: [] /api/v1/graph/investigate/query: post: tags: - graph summary: Run one typed investigation primitive against the event lake description: Execute one pivot. Tenant comes from the session, never the request. operationId: run_investigation_tool_api_v1_graph_investigate_query_post requestBody: content: application/json: schema: $ref: '#/components/schemas/InvestigationToolRequest' required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Run Investigation Tool Api V1 Graph Investigate Query Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/graph/context/import: post: tags: - graph summary: Import identity, organisational and business context into the graph description: 'Upsert context records. Safe to run repeatedly on a schedule. Records are merged rather than replaced: an import runs against a source of record that may be partial, and replacing the tenant''s context with whatever one run produced would delete a department because an HR export timed out. Every rejected record is returned with its reason. A partially-applied import reporting success is worse than a rejected one, because afterwards the gaps are invisible.' operationId: import_graph_context_api_v1_graph_context_import_post requestBody: content: application/json: schema: $ref: '#/components/schemas/ContextImportRequest' required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Import Graph Context Api V1 Graph Context Import Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/graph/neighbors/{entity_type}/{entity_id}: get: tags: - graph summary: Get immediate graph neighbors of an entity description: Return all nodes directly connected (depth 1) to the specified entity. operationId: get_entity_neighbors_api_v1_graph_neighbors__entity_type___entity_id__get security: - HTTPBearer: [] parameters: - name: entity_type in: path required: true schema: type: string title: Entity Type - name: entity_id in: path required: true schema: type: string title: Entity Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/EntityNeighborsResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/graph/mitre-coverage: get: tags: - graph summary: MITRE ATT&CK technique coverage for tenant description: 'Return MITRE ATT&CK technique coverage aggregated from all tenant alerts. When the Neo4j knowledge graph is unreachable (e.g. in the public demo), this endpoint returns an empty list rather than 503-ing, so the Coverage UI degrades gracefully instead of breaking the whole page.' operationId: get_mitre_coverage_api_v1_graph_mitre_coverage_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/MitreCoverageItem' type: array title: Response Get Mitre Coverage Api V1 Graph Mitre Coverage Get security: - HTTPBearer: [] /api/v1/graph/mitre/coverage: get: tags: - graph summary: MITRE ATT&CK coverage (frontend shape) description: 'Aggregated MITRE coverage in the shape the analyst console expects. The console''s :code:`graph.getMitreCoverage()` call hits this URL and expects ``{ tactics, cells, generatedAt }``. We aggregate the per-technique records returned by the same Neo4j-backed service used by ``/graph/mitre-coverage`` and degrade gracefully (empty set) when the knowledge graph is offline, mirroring that endpoint''s behaviour.' operationId: get_mitre_coverage_compat_api_v1_graph_mitre_coverage_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MitreCoverageResponse' security: - HTTPBearer: [] /api/v1/graph/entities/host: post: tags: - graph summary: Upsert a Host node in the graph description: Create or update a Host node in the knowledge graph. operationId: upsert_host_api_v1_graph_entities_host_post requestBody: content: application/json: schema: $ref: '#/components/schemas/UpsertHostRequest' required: true responses: '201': description: Successful Response content: application/json: schema: additionalProperties: type: string type: object title: Response Upsert Host Api V1 Graph Entities Host Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/graph/entities/user: post: tags: - graph summary: Upsert a User node in the graph description: Create or update a User node in the knowledge graph. operationId: upsert_user_api_v1_graph_entities_user_post requestBody: content: application/json: schema: $ref: '#/components/schemas/UpsertUserRequest' required: true responses: '201': description: Successful Response content: application/json: schema: additionalProperties: type: string type: object title: Response Upsert User Api V1 Graph Entities User Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/graph/entities/alert: post: tags: - graph summary: Upsert an Alert node and its relationships in the graph description: 'Create or update an Alert node and link it to Host, User, IOC, and Technique nodes. Called automatically after alert creation to keep the graph in sync.' operationId: upsert_alert_graph_api_v1_graph_entities_alert_post requestBody: content: application/json: schema: $ref: '#/components/schemas/UpsertAlertGraphRequest' required: true responses: '201': description: Successful Response content: application/json: schema: additionalProperties: type: string type: object title: Response Upsert Alert Graph Api V1 Graph Entities Alert Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/graph/entities/case: post: tags: - graph summary: Upsert a Case node and link to alerts in the graph description: Create or update a Case node and link it to Alert nodes. operationId: upsert_case_graph_api_v1_graph_entities_case_post requestBody: content: application/json: schema: $ref: '#/components/schemas/UpsertCaseGraphRequest' required: true responses: '201': description: Successful Response content: application/json: schema: additionalProperties: type: string type: object title: Response Upsert Case Graph Api V1 Graph Entities Case Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/playbooks: get: tags: - playbooks summary: List playbooks operationId: list_playbooks_api_v1_playbooks_get security: - HTTPBearer: [] parameters: - name: enabled_only in: query required: false schema: type: boolean default: false title: Enabled Only responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - playbooks summary: Create playbook operationId: create_playbook_api_v1_playbooks_post security: - HTTPBearer: [] responses: '201': description: Successful Response content: application/json: schema: {} /api/v1/playbooks/runs: get: tags: - playbooks summary: List playbook runs operationId: list_runs_api_v1_playbooks_runs_get security: - HTTPBearer: [] parameters: - name: limit in: query required: false schema: type: integer default: 50 title: Limit responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/playbooks/runs/{run_id}: get: tags: - playbooks summary: Get a playbook run operationId: get_run_api_v1_playbooks_runs__run_id__get security: - HTTPBearer: [] parameters: - name: run_id in: path required: true schema: type: string title: Run Id responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/playbooks/{playbook_id}: get: tags: - playbooks summary: Get a playbook operationId: get_playbook_api_v1_playbooks__playbook_id__get security: - HTTPBearer: [] parameters: - name: playbook_id in: path required: true schema: type: string title: Playbook Id responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' put: tags: - playbooks summary: Update a playbook operationId: update_playbook_api_v1_playbooks__playbook_id__put security: - HTTPBearer: [] parameters: - name: playbook_id in: path required: true schema: type: string title: Playbook Id responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - playbooks summary: Delete a playbook operationId: delete_playbook_api_v1_playbooks__playbook_id__delete security: - HTTPBearer: [] parameters: - name: playbook_id in: path required: true schema: type: string title: Playbook Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/playbooks/{playbook_id}/run: post: tags: - playbooks summary: Execute a playbook operationId: run_playbook_api_v1_playbooks__playbook_id__run_post security: - HTTPBearer: [] parameters: - name: playbook_id in: path required: true schema: type: string title: Playbook Id responses: '202': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/playbook-steps/dispatch: post: tags: - playbooks summary: Run one playbook step through governed action dispatch description: 'Dispatch one step and report what actually happened. Never raises for an action-level outcome. A blocked action, an approval queue, a missing integration and a vendor failure are all 200 with ``executed: false`` and a status naming which one it was, so the engine has a single shape to read. ``HTTPException`` is reserved for callers this route will not serve at all.' operationId: dispatch_playbook_step_api_v1_playbook_steps_dispatch_post security: - HTTPBearer: [] parameters: - name: x-aisoc-service-token in: header required: false schema: anyOf: - type: string - type: 'null' title: X-Aisoc-Service-Token requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/StepDispatchRequest' responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Dispatch Playbook Step Api V1 Playbook Steps Dispatch Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/playbook-steps/references: post: tags: - playbooks summary: Resolve the named integrations a playbook http step addresses description: 'Return the enabled references this tenant has for the names asked for. A name that is absent or disabled is simply not in the response. The engine reports which ones it needed and did not get; answering with an empty string instead would substitute nothing into ``${IDP_BASE_URL}/sessions`` and hand httpx a scheme-relative path. Secret halves (bearer headers, webhook URLs) are returned to the agents service, which is an authenticated internal peer holding the service token, and are used for exactly one request. The engine records header *names* in the run result, never values — the run record is readable by anyone with access to the case.' operationId: resolve_playbook_references_api_v1_playbook_steps_references_post security: - HTTPBearer: [] parameters: - name: x-aisoc-service-token in: header required: false schema: anyOf: - type: string - type: 'null' title: X-Aisoc-Service-Token requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ReferenceLookupRequest' responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Resolve Playbook References Api V1 Playbook Steps References Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/plugins: get: tags: - plugins summary: List Plugins description: List all currently loaded plugins, optionally filtered by type. operationId: list_plugins_api_v1_plugins_get security: - HTTPBearer: [] parameters: - name: plugin_type in: query required: false schema: anyOf: - type: string - type: 'null' title: Plugin Type responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/PluginOut' title: Response List Plugins Api V1 Plugins Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/plugins/discover: post: tags: - plugins summary: Discover Plugins description: Re-scan the plugins directory and load any new plugins found. operationId: discover_plugins_api_v1_plugins_discover_post responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/DiscoverResponse' security: - HTTPBearer: [] /api/v1/plugins/{plugin_id}: get: tags: - plugins summary: Get Plugin description: Get details for a specific plugin. operationId: get_plugin_api_v1_plugins__plugin_id__get security: - HTTPBearer: [] parameters: - name: plugin_id in: path required: true schema: type: string title: Plugin Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PluginOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - plugins summary: Unload Plugin description: Unload a plugin from memory (does not remove files from disk). operationId: unload_plugin_api_v1_plugins__plugin_id__delete security: - HTTPBearer: [] parameters: - name: plugin_id in: path required: true schema: type: string title: Plugin Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/plugins/{plugin_id}/enable: post: tags: - plugins summary: Enable Plugin description: Enable a loaded plugin. operationId: enable_plugin_api_v1_plugins__plugin_id__enable_post security: - HTTPBearer: [] parameters: - name: plugin_id in: path required: true schema: type: string title: Plugin Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PluginOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/plugins/{plugin_id}/disable: post: tags: - plugins summary: Disable Plugin description: Disable a loaded plugin (keeps it in memory but blocks invocations). operationId: disable_plugin_api_v1_plugins__plugin_id__disable_post security: - HTTPBearer: [] parameters: - name: plugin_id in: path required: true schema: type: string title: Plugin Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PluginOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/plugins/{plugin_id}/reload: post: tags: - plugins summary: Reload Plugin description: Unload and re-import a plugin from disk (hot-reload). operationId: reload_plugin_api_v1_plugins__plugin_id__reload_post security: - HTTPBearer: [] parameters: - name: plugin_id in: path required: true schema: type: string title: Plugin Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PluginOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/plugins/{plugin_id}/run: post: tags: - plugins summary: Run Plugin description: 'Directly invoke a plugin. Supports enrichers, actions, and connectors.' operationId: run_plugin_api_v1_plugins__plugin_id__run_post security: - HTTPBearer: [] parameters: - name: plugin_id in: path required: true schema: type: string title: Plugin Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RunRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RunResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/community/publishers/keys: get: tags: - community summary: List Publisher Keys operationId: list_publisher_keys_api_v1_community_publishers_keys_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/PublisherKeyOut' type: array title: Response List Publisher Keys Api V1 Community Publishers Keys Get security: - HTTPBearer: [] post: tags: - community summary: Add Publisher Key description: 'Register a public key you will sign submissions with. Until a key is registered, submissions are accepted and marked unverified. Once one is, a submission that does not match it is rejected — which is the difference between a signature and a decoration.' operationId: add_publisher_key_api_v1_community_publishers_keys_post requestBody: content: application/json: schema: $ref: '#/components/schemas/PublisherKeyIn' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PublisherKeyOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/community/publishers/keys/{fingerprint}: delete: tags: - community summary: Revoke Publisher Key description: 'Revoke a key. Recorded as a timestamp, not a delete: "signed by a key we have since revoked" is a more useful statement after the fact than "unknown key".' operationId: revoke_publisher_key_api_v1_community_publishers_keys__fingerprint__delete security: - HTTPBearer: [] parameters: - name: fingerprint in: path required: true schema: type: string title: Fingerprint responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/community/plugins/publish: post: tags: - community summary: Publish Plugin description: Submit a signed plugin tarball for community review. operationId: publish_plugin_api_v1_community_plugins_publish_post responses: '201': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Publish Plugin Api V1 Community Plugins Publish Post security: - HTTPBearer: [] /api/v1/community/plugins: get: tags: - community summary: List Community Plugins description: Browse approved community plugins. operationId: list_community_plugins_api_v1_community_plugins_get parameters: - name: status in: query required: false schema: anyOf: - type: string - type: 'null' title: Status - name: plugin_type in: query required: false schema: anyOf: - type: string - type: 'null' title: Plugin Type - name: tags in: query required: false schema: anyOf: - type: string - type: 'null' description: Comma-separated tags title: Tags description: Comma-separated tags - name: sort in: query required: false schema: type: string pattern: ^(install_count|rating|submitted_at|name)$ default: install_count title: Sort - name: order in: query required: false schema: type: string pattern: ^(asc|desc)$ default: desc title: Order - 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: 20 title: Page Size responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CommunityPluginListOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/community/plugins/{plugin_id}: get: tags: - community summary: Get Community Plugin description: Get community plugin detail. operationId: get_community_plugin_api_v1_community_plugins__plugin_id__get parameters: - name: plugin_id in: path required: true schema: type: string title: Plugin Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CommunityPluginOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/community/plugins/{plugin_id}/install: post: tags: - community summary: Install Community Plugin description: Install a community plugin to the current instance. operationId: install_community_plugin_api_v1_community_plugins__plugin_id__install_post security: - HTTPBearer: [] parameters: - name: plugin_id in: path required: true schema: type: string title: Plugin Id responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: type: string title: Response Install Community Plugin Api V1 Community Plugins Plugin Id Install Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/community/plugins/{plugin_id}/rate: post: tags: - community summary: Rate Community Plugin description: Rate a community plugin. operationId: rate_community_plugin_api_v1_community_plugins__plugin_id__rate_post security: - HTTPBearer: [] parameters: - name: plugin_id in: path required: true schema: type: string title: Plugin Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RatingIn' responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Rate Community Plugin Api V1 Community Plugins Plugin Id Rate Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/community/plugins/{plugin_id}/review: put: tags: - community summary: Review Community Plugin description: 'Admin: approve or reject a plugin submission.' operationId: review_community_plugin_api_v1_community_plugins__plugin_id__review_put security: - HTTPBearer: [] parameters: - name: plugin_id in: path required: true schema: type: string title: Plugin Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ReviewAction' responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: type: string title: Response Review Community Plugin Api V1 Community Plugins Plugin Id Review Put '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/community/detections/publish: post: tags: - community summary: Publish Detection description: Submit a Sigma detection rule for community review. operationId: publish_detection_api_v1_community_detections_publish_post requestBody: content: text/plain: schema: type: string title: Content required: true responses: '201': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Publish Detection Api V1 Community Detections Publish Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/community/detections: get: tags: - community summary: List Community Detections description: Browse community Sigma detection rules with pagination and filtering. operationId: list_community_detections_api_v1_community_detections_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: 20 title: Page Size - name: search in: query required: false schema: anyOf: - type: string - type: 'null' title: Search - name: logsource_category in: query required: false schema: anyOf: - type: string - type: 'null' title: Logsource Category - name: logsource_product in: query required: false schema: anyOf: - type: string - type: 'null' title: Logsource Product - name: level in: query required: false schema: anyOf: - type: string - type: 'null' title: Level - name: sort_by in: query required: false schema: type: string pattern: ^(install_count|rating|name)$ default: install_count title: Sort By responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response List Community Detections Api V1 Community Detections Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/community/detections/{detection_id}: get: tags: - community summary: Get Community Detection description: Get Sigma rule detail including full YAML content (sigma_yaml field). operationId: get_community_detection_api_v1_community_detections__detection_id__get parameters: - name: detection_id in: path required: true schema: type: string title: Detection Id responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Get Community Detection Api V1 Community Detections Detection Id Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/community/detections/{detection_id}/install: post: tags: - community summary: Install Community Detection description: Install a community detection rule to the tenant. operationId: install_community_detection_api_v1_community_detections__detection_id__install_post security: - HTTPBearer: [] parameters: - name: detection_id in: path required: true schema: type: string title: Detection Id responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: type: string title: Response Install Community Detection Api V1 Community Detections Detection Id Install Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/community/playbooks/submit: post: tags: - community summary: Submit Playbook description: Submit a community playbook. operationId: submit_playbook_api_v1_community_playbooks_submit_post requestBody: content: application/json: schema: additionalProperties: true type: object title: Definition required: true responses: '201': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Submit Playbook Api V1 Community Playbooks Submit Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/community/playbooks: get: tags: - community summary: List Community Playbooks description: Browse approved community playbooks with optional search and sort. operationId: list_community_playbooks_api_v1_community_playbooks_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: 20 title: Page Size - name: search in: query required: false schema: anyOf: - type: string - type: 'null' title: Search - name: sort_by in: query required: false schema: type: string pattern: ^(install_count|rating|name)$ default: install_count title: Sort By responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response List Community Playbooks Api V1 Community Playbooks Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/community/playbooks/{playbook_id}/install: post: tags: - community summary: Install Community Playbook description: Install a community playbook to the tenant. operationId: install_community_playbook_api_v1_community_playbooks__playbook_id__install_post security: - HTTPBearer: [] parameters: - name: playbook_id in: path required: true schema: type: string title: Playbook Id responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: type: string title: Response Install Community Playbook Api V1 Community Playbooks Playbook Id Install Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/community/playbooks/{playbook_id}/curate: put: tags: - community summary: Curate Community Playbook description: 'Admin: approve or reject a playbook submission.' operationId: curate_community_playbook_api_v1_community_playbooks__playbook_id__curate_put security: - HTTPBearer: [] parameters: - name: playbook_id in: path required: true schema: type: string title: Playbook Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ReviewAction' responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: type: string title: Response Curate Community Playbook Api V1 Community Playbooks Playbook Id Curate Put '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/marketplace: get: tags: - marketplace summary: List Marketplace description: Return the marketplace index with the same filters the UI exposes. operationId: list_marketplace_api_v1_marketplace_get security: - HTTPBearer: [] parameters: - name: type in: query required: false schema: anyOf: - enum: - detection - playbook - plugin type: string - type: 'null' title: Type - name: mitre in: query required: false schema: anyOf: - type: string - type: 'null' description: MITRE ATT&CK technique ID, e.g. T1078 or T1078.004 title: Mitre description: MITRE ATT&CK technique ID, e.g. T1078 or T1078.004 - name: severity in: query required: false schema: anyOf: - type: string - type: 'null' title: Severity - name: category in: query required: false schema: anyOf: - type: string - type: 'null' title: Category - name: source in: query required: false schema: anyOf: - enum: - core - community type: string - type: 'null' title: Source - name: sdk in: query required: false schema: anyOf: - enum: - python - go - both type: string - type: 'null' title: Sdk - name: search in: query required: false schema: anyOf: - type: string maxLength: 200 - type: 'null' title: Search responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MarketplaceListResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/marketplace/install: post: tags: - marketplace summary: Install Marketplace Item description: 'Activate a marketplace item for the caller''s tenant. For core (on-disk) items this records that the item is "enabled" for the tenant — the underlying detection/playbook/plugin file is already present on the AiSOC instance and is read straight from disk by the relevant subsystem. The install marker is what the UI needs in order to show ``✓ Installed`` and what tenants can later disable.' operationId: install_marketplace_item_api_v1_marketplace_install_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/InstallRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/InstallResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - marketplace summary: Uninstall Marketplace Item description: Remove a previously installed marketplace item from the tenant. operationId: uninstall_marketplace_item_api_v1_marketplace_install_delete security: - HTTPBearer: [] parameters: - name: type in: query required: true schema: enum: - detection - playbook - plugin type: string title: Type - name: id in: query required: true schema: type: string title: Id responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Uninstall Marketplace Item Api V1 Marketplace Install Delete '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/marketplace/installed: get: tags: - marketplace summary: List Installed description: List all marketplace items installed for the caller's tenant. operationId: list_installed_api_v1_marketplace_installed_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response List Installed Api V1 Marketplace Installed Get security: - HTTPBearer: [] /api/v1/marketplace/mitre: get: tags: - marketplace summary: Mitre Coverage description: 'Return the MITRE ATT&CK coverage block from the marketplace index. Useful for the marketplace UI to render its MITRE filter without walking every item on the client.' operationId: mitre_coverage_api_v1_marketplace_mitre_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Mitre Coverage Api V1 Marketplace Mitre Get security: - HTTPBearer: [] /api/v1/mcp-servers: get: tags: - mcp-servers summary: List Mcp Servers description: Every MCP server registered for the caller's tenant. operationId: list_mcp_servers_api_v1_mcp_servers_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/McpServerListResponse' security: - HTTPBearer: [] post: tags: - mcp-servers summary: Register Mcp Server description: 'Register a server for this tenant. The tenant comes from the credential. A refusal is a 422 naming what was wrong, because every refusal here is something an operator typed.' operationId: register_mcp_server_api_v1_mcp_servers_post requestBody: content: application/json: schema: $ref: '#/components/schemas/McpServerCreate' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/McpServerModel' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/mcp-servers/{server_id}: patch: tags: - mcp-servers summary: Update Mcp Server description: Change a registered server. Absent fields are left alone. operationId: update_mcp_server_api_v1_mcp_servers__server_id__patch security: - HTTPBearer: [] parameters: - name: server_id in: path required: true schema: type: string format: uuid title: Server Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/McpServerUpdate' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/McpServerModel' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - mcp-servers summary: Remove Mcp Server operationId: remove_mcp_server_api_v1_mcp_servers__server_id__delete security: - HTTPBearer: [] parameters: - name: server_id in: path required: true schema: type: string format: uuid title: Server Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mcp/tool-calls/authorize: post: tags: - mcp summary: Authorize Tool Call description: 'Decide one tool call and record the decision. No ``require_permission`` dependency: the permission a call needs depends on the tool, and :func:`app.services.mcp_access.authorize` resolves it from the catalogue and checks it through the principal''s own path. A blanket dependency here would be a second, coarser authority in front of the specific one.' operationId: authorize_tool_call_api_v1_mcp_tool_calls_authorize_post requestBody: content: application/json: schema: $ref: '#/components/schemas/McpAuthorizeRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/McpAuthorizeResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/mcp/tool-calls/{call_id}/complete: post: tags: - mcp summary: Complete Tool Call description: 'Record how a call that was allowed turned out. Authorized against the call being reported on, not by a blanket permission. There is no single permission every MCP caller holds — a key minted for ``alerts:read`` is a legitimate caller — so a blanket dependency here would either be wider than the call it describes or exclude the callers it exists for. Instead this resolves the ``mcp:tool_call`` row the authorize step wrote for this id in this tenant, and requires the principal to still hold the permission *that call* needed. Two consequences, both wanted: a caller cannot write an outcome for a call nobody authorized here, and a key whose scopes were narrowed between the call and the report is refused rather than quietly accepted.' operationId: complete_tool_call_api_v1_mcp_tool_calls__call_id__complete_post security: - HTTPBearer: [] parameters: - name: call_id in: path required: true schema: type: string format: uuid title: Call Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/McpCompleteRequest' responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mcp/tools: get: tags: - mcp summary: List Tools operationId: list_tools_api_v1_mcp_tools_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/McpToolStateModel' type: array title: Response List Tools Api V1 Mcp Tools Get security: - HTTPBearer: [] /api/v1/mcp/tools/{tool_name}: put: tags: - mcp summary: Set Tool Grant operationId: set_tool_grant_api_v1_mcp_tools__tool_name__put security: - HTTPBearer: [] parameters: - name: tool_name in: path required: true schema: type: string title: Tool Name requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/McpToolGrantRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/McpToolStateModel' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mcp/approvals: get: tags: - mcp summary: List Approvals operationId: list_approvals_api_v1_mcp_approvals_get security: - HTTPBearer: [] parameters: - name: state in: query required: false schema: type: string default: pending title: State responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/McpApprovalModel' title: Response List Approvals Api V1 Mcp Approvals Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mcp/approvals/{approval_id}/decide: post: tags: - mcp summary: Decide Approval operationId: decide_approval_api_v1_mcp_approvals__approval_id__decide_post security: - HTTPBearer: [] parameters: - name: approval_id in: path required: true schema: type: string format: uuid title: Approval Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/McpApprovalDecisionRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/McpApprovalModel' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/memory/items: post: tags: - semantic-memory summary: Ingest Item operationId: ingest_item_api_v1_memory_items_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MemoryItemIn' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MemoryIngestResult' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' get: tags: - semantic-memory summary: List Items operationId: list_items_api_v1_memory_items_get security: - HTTPBearer: [] parameters: - name: kind in: query required: false schema: anyOf: - type: string - type: 'null' title: Kind - name: state in: query required: false schema: anyOf: - type: string - type: 'null' title: State - name: limit in: query required: false schema: type: integer maximum: 500 minimum: 1 default: 100 title: Limit responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/MemoryItemOut' title: Response List Items Api V1 Memory Items Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/memory/items/{item_id}: delete: tags: - semantic-memory summary: Retire Item description: Retire, never hard-delete. 'What did the agent know' must stay answerable. operationId: retire_item_api_v1_memory_items__item_id__delete security: - HTTPBearer: [] parameters: - name: item_id in: path required: true schema: type: string format: uuid title: Item Id - name: reason in: query required: false schema: type: string default: '' title: Reason responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MemoryItemOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/memory/recall: post: tags: - semantic-memory summary: Recall description: 'Vector recall over this tenant''s memory. Every hit carries its provenance. ``knowledge_base:read`` is the right permission and it already exists: ``POST /kb/query`` uses it for the full-text half of the same corpus, and every role that triages holds it. Reusing it rather than minting a ``memory:read`` keeps one answer to "may this principal search what the organisation knows" — two permissions over one corpus is two answers that can disagree, and the disagreement surfaces on the day somebody narrows a role.' operationId: recall_api_v1_memory_recall_post requestBody: content: application/json: schema: $ref: '#/components/schemas/MemoryRecallIn' required: true responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/MemoryRecallHit' type: array title: Response Recall Api V1 Memory Recall Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/memory/conflicts: get: tags: - semantic-memory summary: List Conflicts operationId: list_conflicts_api_v1_memory_conflicts_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/MemoryConflictOut' type: array title: Response List Conflicts Api V1 Memory Conflicts Get security: - HTTPBearer: [] /api/v1/memory/conflicts/{item_id}/resolve: post: tags: - semantic-memory summary: Resolve Conflict description: Settle a conflict by naming which side survives. The other is retired. operationId: resolve_conflict_api_v1_memory_conflicts__item_id__resolve_post security: - HTTPBearer: [] parameters: - name: item_id in: path required: true schema: type: string format: uuid title: Item Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MemoryConflictResolution' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MemoryConflictOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/memory/status: get: tags: - semantic-memory summary: Recall Status description: 'Can this deployment recall at all, and over how much. Every count here is computed from rows this request counted, not read from a cached figure, and an unknown stays ``null`` rather than becoming a confident zero.' operationId: recall_status_api_v1_memory_status_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Recall Status Api V1 Memory Status Get security: - HTTPBearer: [] /api/v1/tenant-skills: get: tags: - tenant-skills summary: Every skill this tenant has authored operationId: list_tenant_skills_api_v1_tenant_skills_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SkillListResponse' security: - HTTPBearer: [] put: tags: - tenant-skills summary: Create or update a skill from its YAML description: 'Save the document. A content change bumps the version and drops to draft. ``PUT`` with no id in the path because the id is inside the document: a skill''s identity is the author''s slug, and taking it from a path as well would create two authorities that can disagree.' operationId: save_tenant_skill_api_v1_tenant_skills_put requestBody: content: application/json: schema: $ref: '#/components/schemas/SaveSkillRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SkillModel' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/tenant-skills/{skill_id}: get: tags: - tenant-skills summary: One skill operationId: get_tenant_skill_api_v1_tenant_skills__skill_id__get security: - HTTPBearer: [] parameters: - name: skill_id in: path required: true schema: type: string title: Skill Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SkillModel' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - tenant-skills summary: Delete a skill and its history description: 'For a skill created by mistake. To stop using a working one, retire it. Deleting removes the version history, which is what explains verdicts the skill already steered. The docs say so beside this route.' operationId: delete_tenant_skill_api_v1_tenant_skills__skill_id__delete security: - HTTPBearer: [] parameters: - name: skill_id in: path required: true schema: type: string title: Skill Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/tenant-skills/{skill_id}/versions: get: tags: - tenant-skills summary: Every version of one skill, newest first description: 'Resolve a ``skill@vN`` recorded on an investigation back to its text. This is the read that makes the version recorded on a verdict mean something. Without it the pair is a number pointing at nothing.' operationId: list_skill_versions_api_v1_tenant_skills__skill_id__versions_get security: - HTTPBearer: [] parameters: - name: skill_id in: path required: true schema: type: string title: Skill Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/SkillVersionModel' title: Response List Skill Versions Api V1 Tenant Skills Skill Id Versions Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/tenant-skills/validate: post: tags: - tenant-skills summary: Parse and validate without saving description: 'Back the editor''s validate button with the same call the save makes. A 200 carrying ``valid: false`` rather than a 422: the editor asks this on every keystroke pause, and an error status on a document the author is still typing is a failed request in their network tab every few seconds. The save route returns the 422.' operationId: validate_tenant_skill_api_v1_tenant_skills_validate_post requestBody: content: application/json: schema: $ref: '#/components/schemas/SaveSkillRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ValidateResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/tenant-skills/{skill_id}/backtest: post: tags: - tenant-skills summary: Grade this skill against the tenant's own closed findings, with and without it description: 'Start the two Phase 1 replay runs that grade this version. 202 for the same reason ``POST /evaluations/replay`` is: replaying a few hundred findings through a model twice is not a request-response operation. The ids come back immediately and the console polls both. ``connectors:write`` rather than ``settings:write``, because what this actually does is reach a customer''s own SIEM with stored credentials, which is the bar every other route that does that clears.' operationId: backtest_tenant_skill_api_v1_tenant_skills__skill_id__backtest_post security: - HTTPBearer: [] parameters: - name: skill_id in: path required: true schema: type: string title: Skill Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SkillBacktestRequest' responses: '202': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SkillBacktestResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/tenant-skills/{skill_id}/activate: post: tags: - tenant-skills summary: Put a backtested skill into service description: 'Activate, refusing anything that would put untested guidance in front of alerts. The refusals live in the store and are restated by the database constraint, so neither this route nor a future caller can be the only place the rule holds.' operationId: activate_tenant_skill_api_v1_tenant_skills__skill_id__activate_post security: - HTTPBearer: [] parameters: - name: skill_id in: path required: true schema: type: string title: Skill Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SkillModel' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/tenant-skills/{skill_id}/retire: post: tags: - tenant-skills summary: Take a skill out of service, keeping its history operationId: retire_tenant_skill_api_v1_tenant_skills__skill_id__retire_post security: - HTTPBearer: [] parameters: - name: skill_id in: path required: true schema: type: string title: Skill Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SkillModel' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/rbac/permissions: get: tags: - rbac summary: List Permissions description: List all available permissions. operationId: list_permissions_api_v1_rbac_permissions_get security: - HTTPBearer: [] parameters: - name: category in: query required: false schema: anyOf: - type: string - type: 'null' title: Category responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/PermissionOut' title: Response List Permissions Api V1 Rbac Permissions Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/rbac/roles: get: tags: - rbac summary: List Roles description: 'List all roles for the current tenant, with permissions and headcount. `user_count` counts this tenant''s users holding each role — the Roles screen shows it so an operator can see who would move when a role''s grants change, not just what the grants are.' operationId: list_roles_api_v1_rbac_roles_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/RoleOut' type: array title: Response List Roles Api V1 Rbac Roles Get security: - HTTPBearer: [] post: tags: - rbac summary: Create Role description: 'Create a custom role for the current tenant. `permission_names` (validated against the platform vocabulary, unknown keys → 400 naming them) is the console-facing shape; `permission_ids` stays for API clients. Both may be sent; they merge.' operationId: create_role_api_v1_rbac_roles_post requestBody: content: application/json: schema: $ref: '#/components/schemas/RoleIn' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RoleOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/rbac/roles/seed: post: tags: - rbac summary: Seed Roles description: 'Seed this tenant''s catalog from `app.core.rbac_catalog`. Idempotent — safe to call from the Roles screen''s empty state ("Seed roles") as often as you like. Catalog rows and the membership backfill land in one transaction, so a half-seeded tenant (roles with no members, every member resolving to zero permissions) is not a state this endpoint can leave behind.' operationId: seed_roles_api_v1_rbac_roles_seed_post responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Seed Roles Api V1 Rbac Roles Seed Post security: - HTTPBearer: [] /api/v1/rbac/roles/{role_id}: get: tags: - rbac summary: Get Role operationId: get_role_api_v1_rbac_roles__role_id__get security: - HTTPBearer: [] parameters: - name: role_id in: path required: true schema: type: string format: uuid title: Role Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RoleOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' patch: tags: - rbac summary: Update Role description: 'Edit label/description (and, optionally, replace the permission set). System roles are refused with an explanatory 403 body that names the role: the lock is a server rule, not a hidden button. `name` cannot be edited — see RoleUpdate.' operationId: update_role_api_v1_rbac_roles__role_id__patch security: - HTTPBearer: [] parameters: - name: role_id in: path required: true schema: type: string format: uuid title: Role Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RoleUpdate' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RoleOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - rbac summary: Delete Role operationId: delete_role_api_v1_rbac_roles__role_id__delete security: - HTTPBearer: [] parameters: - name: role_id in: path required: true schema: type: string format: uuid title: Role Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/rbac/roles/{role_id}/permissions: put: tags: - rbac summary: Replace Role Permissions description: 'Replace the role''s full permission set (atomic, audited, before/after). Accepts `{"permissions": ["alerts:read", ...]}` or a bare list `["alerts:read", ...]` — both shapes validate every key against the vocabulary first, so an unknown key is a 400 that names it, never a silently dropped grant.' operationId: replace_role_permissions_api_v1_rbac_roles__role_id__permissions_put security: - HTTPBearer: [] parameters: - name: role_id in: path required: true schema: type: string format: uuid title: Role Id requestBody: content: application/json: schema: $ref: '#/components/schemas/Body_replace_role_permissions_api_v1_rbac_roles__role_id__permissions_put' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RoleOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - rbac summary: Add Role Permission operationId: add_role_permission_api_v1_rbac_roles__role_id__permissions_post security: - HTTPBearer: [] parameters: - name: role_id in: path required: true schema: type: string format: uuid title: Role Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PermissionNameIn' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RoleOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/rbac/roles/{role_id}/permissions/{permission_name}: delete: tags: - rbac summary: Remove Role Permission operationId: remove_role_permission_api_v1_rbac_roles__role_id__permissions__permission_name__delete security: - HTTPBearer: [] parameters: - name: role_id in: path required: true schema: type: string format: uuid title: Role Id - name: permission_name in: path required: true schema: type: string title: Permission Name responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RoleOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/rbac/users/{user_id}/roles: get: tags: - rbac summary: Get User Roles description: List roles assigned to a user within the current tenant. operationId: get_user_roles_api_v1_rbac_users__user_id__roles_get security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string format: uuid title: User Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/UserRoleOut' title: Response Get User Roles Api V1 Rbac Users User Id Roles Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - rbac summary: Assign Role description: Assign a role to a user. operationId: assign_role_api_v1_rbac_users__user_id__roles_post security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string format: uuid title: User Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UserRoleAssignment' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/UserRoleOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/rbac/users/{user_id}/role: put: tags: - rbac summary: Set Primary Role description: "Replace the user's primary role — the console's `Assign role`\ \ action.\n\nMulti-role storage, single-role product: the assignment set becomes\ \ the\none chosen role, and `users.role` — the string the JWT/static path\n\ reads — is mirrored to match, so the two authorization paths cannot\ndisagree\ \ after this call. Takes effect on the target's next request:\nthe permission\ \ cache version bumps, and the verifier re-reads\n`users.role` from the database.\ \ No re-login required.\n\nRefusals, all before any write:\n * 404 target\ \ not in tenant; 400 unknown role name (naming the valid\n set) or disabled\ \ target; 403 this caller may not confer the role's\n permissions; 409\ \ this would empty the tenant of active admins." operationId: set_primary_role_api_v1_rbac_users__user_id__role_put security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string format: uuid title: User Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PrimaryRoleAssignment' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/UserRoleOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/rbac/users/{user_id}/roles/{role_id}: delete: tags: - rbac summary: Revoke Role description: 'Revoke a role from a user. Last-admin lockout guard: revoking the role that IS the tenant''s admin role from the last active holder is refused with 409, same rule as `PUT /users/{id}/role` and `PATCH /tenants/me/users/{id}` — every door that removes management authority checks it, because "one of the doors remembers" is no control at all.' operationId: revoke_role_api_v1_rbac_users__user_id__roles__role_id__delete security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string format: uuid title: User Id - name: role_id in: path required: true schema: type: string format: uuid title: Role Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/elevation/requests: post: tags: - elevation summary: Request Elevation description: 'Ask for a permission for a while. Confers nothing until approved. The row is written with ``approved_by_id`` NULL, and ``resolve_elevation`` requires that column to be set, so a request is inert by construction rather than by a flag somebody could forget to check. The tenant and the requesting user come from the authenticated principal. Neither is a body field: a caller that can name the subject of an elevation can elevate somebody else, and a caller that can name the tenant can elevate inside another one.' operationId: request_elevation_api_v1_elevation_requests_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ElevationRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ElevationOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' get: tags: - elevation summary: List Elevations description: 'Every elevation in this tenant, newest first. Scoped on the authenticated tenant in the ``WHERE`` clause as well as by row-level security, because query-layer scoping is the control that holds when a session is opened without the RLS context.' operationId: list_elevations_api_v1_elevation_requests_get security: - HTTPBearer: [] parameters: - name: state in: query required: false schema: anyOf: - type: string - type: 'null' title: State responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/ElevationOut' title: Response List Elevations Api V1 Elevation Requests Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/elevation/requests/{grant_id}/approve: post: tags: - elevation summary: Approve Elevation description: "Approve a pending request, if you hold what it confers.\n\nTwo\ \ refusals, and they are different questions:\n\n* **separation of duties**\ \ — an approver who is the requester is a\n requester, and the control would\ \ be a log line;\n* **no escalation through the approval door** — the approver\ \ must\n already hold every permission the grant confers, checked by the\ \ same\n function that guards role assignment and key scopes. Without it,\n\ \ ``elevation:approve`` would be a permission that confers every other\n\ \ permission, which is the wildcard by another name." operationId: approve_elevation_api_v1_elevation_requests__grant_id__approve_post security: - HTTPBearer: [] parameters: - name: grant_id in: path required: true schema: type: string format: uuid title: Grant Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ElevationOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/elevation/requests/{grant_id}/revoke: post: tags: - elevation summary: Revoke Elevation description: 'End an elevation now, rather than at its expiry. Gated on ``elevation:approve`` rather than a permission of its own: whoever may confer this authority may take it back, and a separate ``elevation:revoke`` that somebody forgot to grant would mean an elevation nobody present can end.' operationId: revoke_elevation_api_v1_elevation_requests__grant_id__revoke_post security: - HTTPBearer: [] parameters: - name: grant_id in: path required: true schema: type: string format: uuid title: Grant Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ElevationOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/elevation/mine: get: tags: - elevation summary: My Elevations description: 'This principal''s own elevations. Gated on ``elevation:request`` rather than left identity-only: a principal who may not request one has nothing to read here, and an identity-only route is how "is this a valid session?" quietly becomes the whole authorization decision.' operationId: my_elevations_api_v1_elevation_mine_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/ElevationOut' type: array title: Response My Elevations Api V1 Elevation Mine Get security: - HTTPBearer: [] /api/v1/access-conditions/operators: get: tags: - access-conditions summary: Supported Operators description: 'What this deployment can enforce, and why the rest is refused. A console that offered every operator the evaluator implements would let an operator configure a permanent, unexplained denial.' operationId: supported_operators_api_v1_access_conditions_operators_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Supported Operators Api V1 Access Conditions Operators Get security: - HTTPBearer: [] /api/v1/access-conditions: get: tags: - access-conditions summary: List Conditions operationId: list_conditions_api_v1_access_conditions_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/ConditionOut' type: array title: Response List Conditions Api V1 Access Conditions Get security: - HTTPBearer: [] post: tags: - access-conditions summary: Create Condition description: 'Add a condition to this tenant. The tenant comes from the authenticated principal. A condition is a denial, so a caller able to name the tenant could refuse another tenant''s work — the same shape as the MSSP override that let any authenticated user delete a critical detection from any other tenant. `role` is the same argument one level down, and it was unguarded. Scoping a condition to a role is legislating over that role''s exercise of a permission, so the rule is the one the rest of this tree already applies to conferral, turned around: you may not constrain authority you do not yourself hold. Without it a `soc_analyst` holding `access_conditions:write` could store a condition denying `admin` a permission outright, and the only symptom an administrator would see is their own access failing for a reason the console attributes to a rule they cannot edit. Measured against `resolved_permissions`, never the static role map: `require_permission` admitted this caller on the database-backed set, and judging the grant against the broader static one is how six call sites passed this gate while still escalating.' operationId: create_condition_api_v1_access_conditions_post requestBody: content: application/json: schema: $ref: '#/components/schemas/ConditionIn' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ConditionOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/access-conditions/{condition_id}: patch: tags: - access-conditions summary: Set Condition Enabled description: 'Turn a condition on or off without deleting it. Only ``enabled`` is mutable. Editing a stored condition''s operator or value in place would change what an audit entry referring to it meant; a replaced rule is a new row.' operationId: set_condition_enabled_api_v1_access_conditions__condition_id__patch security: - HTTPBearer: [] parameters: - name: condition_id in: path required: true schema: type: string format: uuid title: Condition Id - name: enabled in: query required: true schema: type: boolean title: Enabled responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ConditionOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - access-conditions summary: Delete Condition operationId: delete_condition_api_v1_access_conditions__condition_id__delete security: - HTTPBearer: [] parameters: - name: condition_id in: path required: true schema: type: string format: uuid title: Condition Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/workload-identities: get: tags: - workload-identities summary: List Workload Identities description: 'Every workload credential, with no secret material. ``last_used_at`` is the column that matters operationally: it is what tells an operator a credential they are about to revoke is dead.' operationId: list_workload_identities_api_v1_workload_identities_get responses: '200': description: Successful Response content: application/json: schema: items: additionalProperties: true type: object type: array title: Response List Workload Identities Api V1 Workload Identities Get security: - HTTPBearer: [] post: tags: - workload-identities summary: Create Workload Identity description: Mint a credential for one internal service. operationId: create_workload_identity_api_v1_workload_identities_post requestBody: content: application/json: schema: $ref: '#/components/schemas/WorkloadIn' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/WorkloadCreated' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/workload-identities/{identity_id}/rotate: post: tags: - workload-identities summary: Rotate Workload Identity description: 'Issue a new secret and keep the old one alive for a grace window. Rotation in place is the whole reason the ``previous_*`` columns exist. The only path before was create, update every caller, delete — which is downtime, so most deployments never rotated at all, which is how one leaked log line became permanent internal access.' operationId: rotate_workload_identity_api_v1_workload_identities__identity_id__rotate_post security: - HTTPBearer: [] parameters: - name: identity_id in: path required: true schema: type: string format: uuid title: Identity Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/WorkloadCreated' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/workload-identities/{identity_id}: delete: tags: - workload-identities summary: Revoke Workload Identity description: 'Revoke immediately, including the superseded secret. Clearing ``previous_hash`` is the half that is easy to miss: a revocation that left a rotation''s grace window intact would leave the credential working for up to a day after an operator believed they had killed it.' operationId: revoke_workload_identity_api_v1_workload_identities__identity_id__delete security: - HTTPBearer: [] parameters: - name: identity_id in: path required: true schema: type: string format: uuid title: Identity Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/admin/users: get: tags: - admin-users summary: List Users description: Paginated member list with live roles, provider, and headcounts. operationId: list_users_api_v1_admin_users_get security: - HTTPBearer: [] parameters: - name: search in: query required: false schema: anyOf: - type: string maxLength: 200 - type: 'null' title: Search - name: role in: query required: false schema: anyOf: - type: string maxLength: 100 - type: 'null' title: Role - name: status in: query required: false schema: anyOf: - type: string pattern: ^(active|disabled)$ - type: 'null' title: Status - name: sort in: query required: false schema: type: string pattern: ^(name|email|status|created|last_login)$ default: created title: Sort - name: order in: query required: false schema: type: string pattern: ^(asc|desc)$ default: desc title: Order - 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/UserPage' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/admin/users/{user_id}: get: tags: - admin-users summary: Get User operationId: get_user_api_v1_admin_users__user_id__get security: - HTTPBearer: [] parameters: - 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/AdminUserOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' patch: tags: - admin-users summary: Patch Status description: 'Enable / disable a member. Disabling ends their sessions immediately; the role assignment is preserved untouched.' operationId: patch_status_api_v1_admin_users__user_id__patch security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string format: uuid title: User Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UserPatch' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AdminUserOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - admin-users summary: Delete User description: "Permanently delete a member.\n\nHard delete, deliberately: `audit_log.actor_id`\ \ and every other\nattribution column SET NULL on delete, so the audit trail\ \ keeps each\naction with the name attributed in the audit payload below —\ \ the\nhistory survives, the identity row does not. Ownership rows\n(`user_roles`,\ \ `aisoc_sso_identities`, `saved_views`, …) cascade.\n\nGuards, in order:\n\ * a member cannot delete their own account (disable yourself instead);\n*\ \ the last active admin cannot be deleted (`_guard_last_admin`);\n* sessions\ \ are ended before the row goes, so any in-flight token dies\n at the next\ \ request even if a race briefly revives the row.\n\nAn SSO-provisioned account\ \ that is deleted will be re-created as a\n`viewer` by JIT on the next successful\ \ login — deletion removes the\naccount, not the IdP's right to provision\ \ it. Say so in the reason if\nthat matters for the case." operationId: delete_user_api_v1_admin_users__user_id__delete security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string format: uuid title: User Id - name: reason in: query required: true schema: type: string minLength: 1 maxLength: 500 title: Reason responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/admin/users/{user_id}/effective-permissions: get: tags: - admin-users summary: Effective Permissions description: 'Merged permission set for one user — the modal''s read-only preview. Answers from `user_roles` only (the catalog), not the static map: what the modal shows is what the enforcement layer will check once the tenant is catalog-backed.' operationId: effective_permissions_api_v1_admin_users__user_id__effective_permissions_get security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string format: uuid title: User Id responses: '200': description: Successful Response content: application/json: schema: type: array items: type: string title: Response Effective Permissions Api V1 Admin Users User Id Effective Permissions Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/admin/users/{user_id}/roles: put: tags: - admin-users summary: Set Roles description: Replace the user's full role set (multi-role replace semantics). operationId: set_roles_api_v1_admin_users__user_id__roles_put security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string format: uuid title: User Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RoleSetIn' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AdminUserOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - admin-users summary: Add Role operationId: add_role_api_v1_admin_users__user_id__roles_post security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string format: uuid title: User Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RoleAddIn' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AdminUserOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/admin/users/{user_id}/roles/{role_name}: delete: tags: - admin-users summary: Remove Role operationId: remove_role_api_v1_admin_users__user_id__roles__role_name__delete security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string format: uuid title: User Id - name: role_name in: path required: true schema: type: string title: Role Name responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AdminUserOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/audit: get: tags: - audit summary: List Audit Events description: Return a paginated, tenant-scoped audit trail. operationId: list_audit_events_api_v1_audit_get security: - HTTPBearer: [] 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: 200 minimum: 1 default: 50 title: Page Size - name: action in: query required: false schema: anyOf: - type: string - type: 'null' description: Filter by action prefix, e.g. 'cases:' title: Action description: Filter by action prefix, e.g. 'cases:' - name: resource in: query required: false schema: anyOf: - type: string - type: 'null' title: Resource - name: actor_id in: query required: false schema: anyOf: - type: string format: uuid - type: 'null' title: Actor Id - name: search in: query required: false schema: anyOf: - type: string - type: 'null' description: Search actor_email or action title: Search description: Search actor_email or action 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' /api/v1/audit/export: get: tags: - audit summary: Export the (filtered) audit trail as CSV or print-ready HTML description: "Return the filtered audit trail as a downloadable artefact.\n\n\ * ``format=csv`` (default) — RFC 4180 CSV, suitable for SIEM ingest\n and\ \ compliance binders.\n* ``format=html`` — self-contained HTML the browser\ \ can save to PDF.\n\nThe same filters as ``GET /audit`` apply, so the exported\ \ bundle\nmatches what the analyst is looking at in the UI. A hard ``limit``\n\ of 10 000 rows keeps a single export bounded — narrow the filters\nfor very\ \ long-running tenants." operationId: export_audit_events_api_v1_audit_export_get security: - HTTPBearer: [] parameters: - name: format in: query required: false schema: type: string pattern: ^(csv|html)$ default: csv title: Format - name: action in: query required: false schema: anyOf: - type: string - type: 'null' description: Filter by action prefix, e.g. 'cases:' title: Action description: Filter by action prefix, e.g. 'cases:' - name: resource in: query required: false schema: anyOf: - type: string - type: 'null' title: Resource - name: actor_id in: query required: false schema: anyOf: - type: string format: uuid - type: 'null' title: Actor Id - name: search in: query required: false schema: anyOf: - type: string - type: 'null' description: Search actor_email or action title: Search description: Search actor_email or action - name: limit in: query required: false schema: type: integer maximum: 10000 minimum: 1 default: 10000 title: Limit responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/branding: get: tags: - branding summary: Get Branding description: 'What the product looks like for the caller''s tenant. Readable by any authenticated member. The console calls it on every page load, and gating it behind an administrative permission would leave an analyst looking at a differently-branded product from their colleague.' operationId: get_branding_api_v1_branding_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Get Branding Api V1 Branding Get security: - HTTPBearer: [] put: tags: - branding summary: Put Branding description: Set branding for the organisation the caller's tenant belongs to. operationId: put_branding_api_v1_branding_put requestBody: content: application/json: schema: $ref: '#/components/schemas/BrandingIn' required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Put Branding Api V1 Branding Put '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/branding/assets/{kind}: post: tags: - branding summary: Upload Asset description: 'Upload a logo or favicon. An SVG goes through the sanitiser and what is stored is the sanitised output. Nothing re-sanitises at render time, so this handler is the only writer and the stored bytes are trusted thereafter.' operationId: upload_asset_api_v1_branding_assets__kind__post security: - HTTPBearer: [] parameters: - name: kind in: path required: true schema: type: string title: Kind requestBody: required: true content: multipart/form-data: schema: $ref: '#/components/schemas/Body_upload_asset_api_v1_branding_assets__kind__post' responses: '201': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Upload Asset Api V1 Branding Assets Kind Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - branding summary: Delete Asset description: Remove an asset, returning that surface to the wordmark. operationId: delete_asset_api_v1_branding_assets__kind__delete security: - HTTPBearer: [] parameters: - name: kind in: path required: true schema: type: string title: Kind responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/branding/assets/{asset_id}: get: tags: - branding summary: Get Asset description: 'Serve a brand asset from this deployment. Scoped to the caller''s own organisation. The bytes are not secret, but an unscoped read would let any authenticated user enumerate every operator''s logo, which is a customer list.' operationId: get_asset_api_v1_branding_assets__asset_id__get security: - HTTPBearer: [] parameters: - name: asset_id in: path required: true schema: type: string format: uuid title: Asset Id responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/compliance/frameworks: get: tags: - compliance summary: List frameworks and controls operationId: list_frameworks_api_v1_compliance_frameworks_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/FrameworksResponse' /api/v1/compliance/frameworks/{framework_id}/controls: get: tags: - compliance summary: List controls for a framework operationId: list_framework_controls_api_v1_compliance_frameworks__framework_id__controls_get parameters: - name: framework_id in: path required: true schema: type: string title: Framework Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ControlsResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/compliance/evidence/collect: post: tags: - compliance summary: Trigger evidence collection job operationId: trigger_evidence_collection_api_v1_compliance_evidence_collect_post requestBody: content: application/json: schema: $ref: '#/components/schemas/CollectJobRequest' required: true responses: '202': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CollectJobResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/compliance/evidence: post: tags: - compliance summary: Collect evidence item operationId: collect_evidence_api_v1_compliance_evidence_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CollectEvidenceRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/EvidenceResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' get: tags: - compliance summary: List evidence items operationId: list_evidence_api_v1_compliance_evidence_get security: - HTTPBearer: [] parameters: - name: framework in: query required: false schema: anyOf: - type: string - type: 'null' title: Framework - name: control_id in: query required: false schema: anyOf: - type: string - type: 'null' title: Control Id - name: case_id in: query required: false schema: anyOf: - type: string format: uuid - type: 'null' title: Case Id - name: status in: query required: false schema: anyOf: - type: string - type: 'null' title: Status - name: limit in: query required: false schema: type: integer maximum: 1000 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/EvidenceResponse' title: Response List Evidence Api V1 Compliance Evidence Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/compliance/evidence/{evidence_id}: get: tags: - compliance summary: Get evidence item operationId: get_evidence_api_v1_compliance_evidence__evidence_id__get security: - HTTPBearer: [] parameters: - name: evidence_id in: path required: true schema: type: string format: uuid title: Evidence Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/EvidenceResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/compliance/evidence/{evidence_id}/review: post: tags: - compliance summary: Accept or reject evidence operationId: review_evidence_api_v1_compliance_evidence__evidence_id__review_post security: - HTTPBearer: [] parameters: - name: evidence_id in: path required: true schema: type: string format: uuid title: Evidence Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ReviewEvidenceRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/EvidenceResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/compliance/report: get: tags: - compliance summary: Generate compliance posture report operationId: compliance_report_api_v1_compliance_report_get security: - HTTPBearer: [] parameters: - name: framework in: query required: false schema: anyOf: - type: string - type: 'null' description: Filter to a single framework. title: Framework description: Filter to a single framework. responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/CompliancePosture' title: Response Compliance Report Api V1 Compliance Report Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/compliance/{framework}: get: tags: - compliance summary: One framework's control status operationId: framework_detail_api_v1_compliance__framework__get security: - HTTPBearer: [] parameters: - name: framework in: path required: true schema: type: string title: Framework responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/FrameworkDetail' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/compliance/{framework}/heatmap: get: tags: - compliance summary: Control status grid operationId: framework_heatmap_api_v1_compliance__framework__heatmap_get security: - HTTPBearer: [] parameters: - name: framework in: path required: true schema: type: string title: Framework responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/HeatmapResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/compliance/{framework}/collect: post: tags: - compliance summary: Collect what the platform can attest for this framework description: 'Write a real evidence row per automatable control. Rows join the same tamper-evident hash chain as manually collected evidence, and land as `pending`: the platform attesting its own state is a claim an auditor still has to accept, and auto-accepting would make the review step decorative.' operationId: framework_collect_api_v1_compliance__framework__collect_post security: - HTTPBearer: [] parameters: - name: framework in: path required: true schema: type: string title: Framework responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CollectResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/compliance/{framework}/export: get: tags: - compliance summary: Export this framework's evidence operationId: framework_export_api_v1_compliance__framework__export_get security: - HTTPBearer: [] parameters: - name: framework in: path required: true schema: type: string title: Framework - name: fmt in: query required: false schema: type: string pattern: ^(csv|json)$ default: csv title: Fmt responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/metrics/dashboard: get: tags: - metrics summary: Get Dashboard Metrics description: 'Return aggregated KPI metrics for the dashboard overview tiles. `period` drives every windowed figure below. It did not exist: the console shipped a global time-window selector whose only consumer was itself, so changing it re-rendered a header and fetched nothing. The trend chart and the "closed this week" tile were fixed at 7 days whatever the control said. Counts of *current state* -- open alerts by severity -- are deliberately not windowed. "How many criticals are open" is a question about now, and answering it for the last hour would hide the backlog rather than scope it.' operationId: get_dashboard_metrics_api_v1_metrics_dashboard_get security: - HTTPBearer: [] parameters: - name: period in: query required: false schema: type: string pattern: ^(1h|24h|7d|30d)$ description: Time window for this view. default: 24h title: Period description: Time window for this view. responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/DashboardMetrics' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/metrics/soc: get: tags: - metrics summary: Get Soc Metrics description: "Return SOC-level KPIs, ATT&CK heatmap, and confidence calibration\ \ curve.\n\nTier 1.4 metrics:\n - MTTD, MTTR, MTTC, FPR, escalation rate,\ \ analyst overrides\n - ATT&CK technique × tactic heatmap\n - Confidence\ \ calibration over time (reliability curve)" operationId: get_soc_metrics_api_v1_metrics_soc_get security: - HTTPBearer: [] parameters: - name: period in: query required: false schema: type: string pattern: ^(1h|24h|7d|30d)$ description: Time window for this view. default: 24h title: Period description: Time window for this view. responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SOCMetrics' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/metrics/alerts/trend: get: tags: - metrics summary: Get Alert Trend description: Return alert count trend data bucketed by time period. operationId: get_alert_trend_api_v1_metrics_alerts_trend_get security: - HTTPBearer: [] parameters: - name: period in: query required: false schema: type: string default: 24h title: Period responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Get Alert Trend Api V1 Metrics Alerts Trend Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/metrics/funnel: get: tags: - metrics summary: Get Funnel Metrics description: 'Return the live tenant funnel for the SOC Console (v1.5). Funnel: events_of_interest → correlation_instances → alerts_generated → analyst_queue_depth, plus efficiency ratios and MITRE coverage. ``deltas`` compares the current window to the immediately preceding window of the same length (e.g. for ``period=24h`` we compare to the 24h ending 24h ago). Values are percentage changes rounded to two decimals; a delta of ``None`` means "no baseline" (the previous window was empty).' operationId: get_funnel_metrics_api_v1_metrics_funnel_get security: - HTTPBearer: [] parameters: - name: period in: query required: false schema: type: string pattern: ^(1h|24h|7d|30d)$ description: Time window for this view. default: 24h title: Period description: Time window for this view. responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/FunnelMetrics' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/health/pipeline: get: tags: - health summary: Get Pipeline Health description: 'Return a 5-stage health snapshot of the SOC pipeline for the tenant. Stages: ``ingest → normalize → fuse → correlate → alert``. See the module docstring for what each cell measures and why some columns are intentionally ``0`` until deeper instrumentation lands. The window for latency / backlog computations is the last hour. The ``status`` ladder is ``unknown | green | yellow | red`` and follows the same convention as ``app.services.connector_freshness``.' operationId: get_pipeline_health_api_v1_health_pipeline_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PipelineHealth' security: - HTTPBearer: [] /api/v1/health/fleet: get: tags: - health summary: Get Fleet Health description: 'Which connectors have quietly stopped working. Every field this reads was already being written — `last_sync`, `error_count`, `oauth_refresh_failures`, `last_schema_drift_at` — and nothing read them together. That produces the failure this platform is least able to tolerate: a connector stops polling, alerts from that source stop arriving, and the console looks calm, because an absence of alerts is indistinguishable from an absence of threats. Staleness is judged per connector against its own configured cadence. A single global threshold would page constantly on daily connectors or stay silent on five-minute ones, and a surface that pages constantly is a surface that gets muted.' operationId: get_fleet_health_api_v1_health_fleet_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Get Fleet Health Api V1 Health Fleet Get security: - HTTPBearer: [] /api/v1/health/dead-letters: get: tags: - health summary: Get Dead Letters description: 'Events the pipeline refused, and why. Three DLQ implementations existed and none could answer this: one wrote a log line, one wrote to a Kafka topic with no consumer, one forgot on restart. Events were being dropped correctly and invisibly — and an invisible drop is indistinguishable from an event that never arrived, which is the worse of the two and the one nobody investigates. Returns the breakdown by reason as well as the rows. A list of fifty dropped events is data; "forty-eight of them failed schema validation on the same topic" is a finding.' operationId: get_dead_letters_api_v1_health_dead_letters_get security: - HTTPBearer: [] parameters: - name: limit in: query required: false schema: type: integer default: 50 title: Limit - name: hours in: query required: false schema: type: integer default: 24 title: Hours responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Get Dead Letters Api V1 Health Dead Letters Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/health/audit-chain: get: tags: - health summary: Get Audit Chain Health description: 'Whether the audit log''s tamper-evidence actually covers this tenant. `apps/docs/docs/operations/security.md` states that every state-changing action is appended to an immutable, hash-chained log. That claim is only true of rows that carry a hash, and the one signal that a row did not was a `logger.warning` — which fired on every `alerts.explain` against a default install for as long as a best-effort cost write could abort the transaction underneath it. Nobody noticed, because an append-only log going quiet looks exactly like an idle one. So the question gets a surface. `unchained` counts rows this tenant holds with no `entry_hash`, split into the ones written before migration 043 (which never had one and are not a defect) and the ones written after it (which are). A reviewer asking "is the chain intact?" gets a number rather than a grep. `verified` replays the chain with `verify_chain` over the most recent window, so a row that was rewritten in place is found rather than assumed absent. Since migration 074 the replay also separates breaks by `chain_epoch`. Epoch 1 is the pre-074 unserialized writer, which could fork two audit rows of one request onto the same predecessor; those rows were left exactly as written rather than re-chained, so a healthy deployment can legitimately hold epoch-1 breaks forever. An epoch-2 break cannot be historical and is the number worth alerting on.' operationId: get_audit_chain_health_api_v1_health_audit_chain_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Get Audit Chain Health Api V1 Health Audit Chain Get security: - HTTPBearer: [] /api/v1/health/shadow-reconciliation: get: tags: - health summary: Get Shadow Reconciliation Health description: "Whether closures made in this tenant's own SIEM are being polled\ \ back.\n\nGap-closure Phase 2.1 (D15).\n\nA sweep that silently stopped and\ \ a sweep with nothing to do look identical\nfrom outside, and that ambiguity\ \ is what made the original gap invisible:\nthe reconciler existed, nothing\ \ called it, and the only symptom available\nto an operator was a scorecard\ \ that never filled in. So this reports the\nsubscription in every state,\ \ including the healthy one and the idle one,\nand names which it is.\n\n\ ``state`` is one of:\n\n``disabled`` the operator has not switched the\ \ sweep on. Not a fault,\n and not a healthy idle sweep either.\n\ ``not_measuring`` the sweep runs, but this tenant has no alert class in\n\ \ shadow mode, so there is nothing to reconcile.\n``no_connector``\ \ this tenant is measuring and has no enabled connector of\n \ \ a type with a closed-finding reader. Agreement is being\n \ \ measured on AiSOC closures only, which is the honest answer\n \ \ and is stated rather than left to be inferred.\n``blocked`` \ \ at least one connector needs an operator. The reason is a\n \ \ sentence, and it will not clear by waiting.\n``degraded`` at least\ \ one connector is failing transiently and is being\n retried.\n\ ``ok`` every configured connector polled." operationId: get_shadow_reconciliation_health_api_v1_health_shadow_reconciliation_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Get Shadow Reconciliation Health Api V1 Health Shadow Reconciliation Get security: - HTTPBearer: [] /api/v1/health/dead-letters/replay: post: tags: - health summary: Replay Dead Letters description: 'Re-read a bounded range of refused messages and replay what now passes. The action that follows a dead-letter queue, and the one Phase 5 left undone: the backlog was reportable and nothing could drain it. Safe rather than merely possible, in four ways. *Deliberate.* The range is `(topic, partition, start_offset)`, supplied by the caller. There is no "replay the backlog" — the excerpt stored on a dead-letter row is a truncated triage record, not the event, so the replay re-reads the real message from Kafka and the operator says which. *Bounded.* `max_messages` is capped at 1000 by this request model, again by the fusion service, and again by a CHECK constraint on the audit table. A bound in one place is a bound the next caller skips. *Authorised and attributable.* `connectors:write` rather than the identity-only dependency the sibling GET carries, because this one re-injects production traffic. The row records who asked. *Observable, and honest about failure.* One `aisoc_dlq_replays` row per request including dry runs, written before fusion is called so an attempt that hangs still left evidence, and a failed replay reports the reason rather than a zero that reads like "nothing to do". The property that makes it safe at all is in fusion: every message is re-validated by the validator that refused it, and one that still fails is refused again instead of being produced. Replaying a poison batch into the consumer that rejected it reproduces the outage, so a preview whose `would_pass` is zero is the answer "your fix has not landed". Defaults to a dry run. Pass `execute: true` once the preview is clean.' operationId: replay_dead_letters_api_v1_health_dead_letters_replay_post requestBody: content: application/json: schema: $ref: '#/components/schemas/DlqReplayRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/DlqReplayResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/health: get: tags: - health summary: Health Liveness description: 'Liveness: the process answers HTTP. Nothing else is claimed. Deliberately touches no store — a liveness probe that fails when a dependency is down invites the orchestrator to restart a healthy process, which converts an outage into an outage plus a restart storm. Whether the service can do its *job* is ``/ready``''s question.' operationId: health_liveness_api_v1_health_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Health Liveness Api V1 Health Get /api/v1/health/ready: get: tags: - health summary: Health Ready description: 'Readiness: can this service do its job, named per dependency. ``database`` is **block**: the API is the system of record''s front door, so without Postgres the status code goes 503 and a load balancer should stop sending traffic here. The body still names every dependency — failing *with the reason* is the whole point, and the chaos grader reads the keys out of the 503 body. ``cache`` is **degrade**: Redis is a cache and every read falls through to its source, so its loss is reported (`cache: false`) while the status stays 200 and the service keeps serving. Failing readiness over a cache would pull the API out of the load balancer to fix a cache.' operationId: health_ready_api_v1_health_ready_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Health Ready Api V1 Health Ready Get /api/v1/insights/soc: get: tags: - insights summary: SOC Insights dashboard aggregate (T3.1) description: 'Return the seven-tile SOC Insights payload for the tenant. Tenant scoping is enforced by ``current_user.tenant_id`` plus an explicit ``tenant_id`` predicate in every aggregate, so the payload is safe to render for any role that can read the dashboard.' operationId: get_soc_insights_api_v1_insights_soc_get security: - HTTPBearer: [] parameters: - name: window in: query required: false schema: enum: - 24h - 7d - 30d type: string description: Rolling window. One of 24h, 7d, 30d. default: 24h title: Window description: Rolling window. One of 24h, 7d, 30d. responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SOCInsightsResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/sla/metrics: get: tags: - sla summary: Get Sla Metrics description: Return aggregated MTTD / MTTR / MTTC metrics for the tenant. operationId: get_sla_metrics_api_v1_sla_metrics_get security: - HTTPBearer: [] parameters: - name: days in: query required: false schema: type: integer maximum: 365 minimum: 1 description: Look-back window in days default: 30 title: Days description: Look-back window in days responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/sla/kpi-targets: get: tags: - sla summary: Get Kpi Bar Targets description: Return merged 2026 KPI bar targets (published defaults plus tenant ``settings.kpi_bar``). operationId: get_kpi_bar_targets_api_v1_sla_kpi_targets_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/KpiBarTargetsOut' security: - HTTPBearer: [] put: tags: - sla summary: Put Kpi Bar Targets description: Patch tenant KPI bar targets; unspecified fields keep previous values. operationId: put_kpi_bar_targets_api_v1_sla_kpi_targets_put requestBody: content: application/json: schema: $ref: '#/components/schemas/KpiBarTargetsUpdate' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/KpiBarTargetsOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/sla/config: get: tags: - sla summary: Get Sla Config description: Return per-severity SLA configuration for the tenant. operationId: get_sla_config_api_v1_sla_config_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/SLAConfigOut' type: array title: Response Get Sla Config Api V1 Sla Config Get security: - HTTPBearer: [] /api/v1/sla/config/{severity}: put: tags: - sla summary: Update Sla Config description: Upsert SLA targets for a given severity. operationId: update_sla_config_api_v1_sla_config__severity__put security: - HTTPBearer: [] parameters: - name: severity in: path required: true schema: type: string title: Severity requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SLAConfigUpdate' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SLAConfigOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/sla/events: post: tags: - sla summary: Record Sla Event description: Record a lifecycle event (detected/acknowledged/resolved/closed) for an alert. operationId: record_sla_event_api_v1_sla_events_post requestBody: content: application/json: schema: $ref: '#/components/schemas/SLAEventCreate' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SLAEventOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/investigations: get: tags: - investigations summary: List Runs description: List recent investigation runs for the caller's tenant. operationId: list_runs_api_v1_investigations_get security: - HTTPBearer: [] parameters: - name: case_id in: query required: false schema: anyOf: - type: string - type: 'null' description: Filter by external case id title: Case Id description: Filter by external case id - name: status in: query required: false schema: anyOf: - type: string - type: 'null' description: 'Filter by status: running | completed | failed' title: Status description: 'Filter by status: running | completed | failed' - 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: array items: $ref: '#/components/schemas/RunSummary' title: Response List Runs Api V1 Investigations Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/investigations/costs/aggregate: get: tags: - investigations summary: Aggregate Costs description: 'Aggregate LLM spend across investigation runs for this tenant. Joins ``aisoc_run_costs`` against ``investigation_runs`` on ``run_id`` and filters by ``investigation_runs.tenant_id``. We deliberately do NOT filter on ``aisoc_run_costs.tenant_id`` directly: the agents service writes whatever ``tenant_id`` string the request carried (e.g. the slug ``"default"``), which need not match the API-side UUID. Anchoring on the run''s canonical tenant column keeps the answer correct regardless of how the cost row was tagged, and cannot leak across tenants because ``investigation_runs`` is RLS-bound and we re-filter explicitly. Uses Postgres ``GROUPING SETS`` to compute per-model rows and the grand total in one round-trip; ``COUNT(DISTINCT run_id)`` then yields a correct run count for both groupings (a naive ``SUM(runs)`` across models would double-count runs that touched more than one model).' operationId: aggregate_costs_api_v1_investigations_costs_aggregate_get security: - HTTPBearer: [] parameters: - name: window_days in: query required: false schema: type: integer maximum: 365 minimum: 1 description: Look-back window in days (anchored on run start time) default: 30 title: Window Days description: Look-back window in days (anchored on run start time) responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CostAggregateResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/investigations/{run_id}: get: tags: - investigations summary: Get Run description: Run summary plus count of attached events and artifacts. operationId: get_run_api_v1_investigations__run_id__get security: - HTTPBearer: [] parameters: - name: run_id in: path required: true schema: type: string format: uuid title: Run Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RunDetail' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/investigations/{run_id}/events: get: tags: - investigations summary: List Events description: 'Paginated event stream for a run. The ``since`` cursor lets clients tail the stream — pass the previously returned ``next_seq`` to fetch the next page without overlap.' operationId: list_events_api_v1_investigations__run_id__events_get security: - HTTPBearer: [] parameters: - name: run_id in: path required: true schema: type: string format: uuid title: Run Id - name: since in: query required: false schema: anyOf: - type: integer minimum: 0 - type: 'null' description: Return only events with seq strictly greater than this value (long-poll) title: Since description: Return only events with seq strictly greater than this value (long-poll) - 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: $ref: '#/components/schemas/EventListResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/investigations/{run_id}/replay: get: tags: - investigations summary: Replay Run description: 'Return the full ordered event list for a run. Bounded by ``max_events`` to keep responses sane; runs that exceed the bound should fall back to the paginated ``/events`` endpoint.' operationId: replay_run_api_v1_investigations__run_id__replay_get security: - HTTPBearer: [] parameters: - name: run_id in: path required: true schema: type: string format: uuid title: Run Id - name: max_events in: query required: false schema: type: integer maximum: 50000 minimum: 1 default: 10000 title: Max Events responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/EventOut' title: Response Replay Run Api V1 Investigations Run Id Replay Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/investigations/{run_id}/explain: get: tags: - investigations summary: Explain Step description: 'Return prompt + response + evidence for a single step. Renders three events for context: the previous decision that led into this step, the focal event, and the next decision. All artifacts attached to the focal event are inlined so the auditor sees the literal LLM transcript.' operationId: explain_step_api_v1_investigations__run_id__explain_get security: - HTTPBearer: [] parameters: - name: run_id in: path required: true schema: type: string format: uuid title: Run Id - name: step in: query required: true schema: type: integer minimum: 0 description: Event seq to explain title: Step description: Event seq to explain responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ExplainResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/investigations/{run_id}/artifacts: get: tags: - investigations summary: List Artifacts operationId: list_artifacts_api_v1_investigations__run_id__artifacts_get security: - HTTPBearer: [] parameters: - name: run_id in: path required: true schema: type: string format: uuid title: Run Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/ArtifactSummary' title: Response List Artifacts Api V1 Investigations Run Id Artifacts Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/investigations/{run_id}/artifacts/{artifact_id}: get: tags: - investigations summary: Get Artifact operationId: get_artifact_api_v1_investigations__run_id__artifacts__artifact_id__get security: - HTTPBearer: [] parameters: - name: run_id in: path required: true schema: type: string format: uuid title: Run Id - name: artifact_id in: path required: true schema: type: string format: uuid title: Artifact Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ArtifactDetail' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/investigations/{run_id}/timeline: get: tags: - investigations summary: Get Timeline description: "Return a scrubbable investigation timeline with decision-provenance\ \ annotations.\n\nEach node carries:\n* ``decision`` — structured provenance\ \ (reason, confidence, next phase,\n tool name/args/result) extracted from\ \ the event ``payload``.\n* ``has_artifact`` — whether a detailed transcript\ \ artifact is attached.\n* ``diff_vs_prev_attempt`` — one-line diff for repeated\ \ steps so analysts\n can see how the agent corrected itself across retry\ \ attempts.\n\nThe response is bounded at 10 000 events. Use the paginated\ \ ``/events``\nendpoint for runs that exceed this." operationId: get_timeline_api_v1_investigations__run_id__timeline_get security: - HTTPBearer: [] parameters: - name: run_id in: path required: true schema: type: string format: uuid title: Run Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/app__api__v1__endpoints__investigations__TimelineResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/investigations/{run_id}/close: post: tags: - investigations summary: Close Investigation description: 'Close an investigation run and persist an auto-generated summary artifact. Idempotent: if the run is already closed a summary artifact is still generated and returned (useful for re-generating the PDF). Status transitions: ``running | completed | failed`` → ``closed``' operationId: close_investigation_api_v1_investigations__run_id__close_post security: - HTTPBearer: [] parameters: - name: run_id in: path required: true schema: type: string format: uuid title: Run Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CloseRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ClosedSummary' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/investigations/{run_id}/summary.pdf: get: tags: - investigations summary: Export Summary Pdf description: 'Stream the investigation summary as a PDF. Looks for the most recent ``investigation-summary`` artifact. If none exists yet the caller should ``POST /close`` first. The PDF is generated server-side using pure-Python ``reportlab`` so there are no browser or headless-Chrome dependencies.' operationId: export_summary_pdf_api_v1_investigations__run_id__summary_pdf_get security: - HTTPBearer: [] parameters: - name: run_id in: path required: true schema: type: string format: uuid title: Run Id responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/investigations/{run_id}/bundle: get: tags: - investigations summary: Export Evidence Bundle description: 'Export this investigation as a signed, replayable evidence bundle. Two exports of one run produce identical bytes, so an auditor can diff them, re-hash them, and check the signature without trusting this endpoint. Prompts travel as digests rather than text, because a bundle is the artefact most likely to leave the customer''s control and a prompt carries their hostnames and usernames. Served as a download rather than JSON so what the auditor verifies is the same byte sequence the signature was computed over. A client that re-serialised a parsed body would change the bytes and the signature would stop matching for a reason nobody could see.' operationId: export_evidence_bundle_api_v1_investigations__run_id__bundle_get security: - HTTPBearer: [] parameters: - name: run_id in: path required: true schema: type: string format: uuid title: Run Id responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/kill-switch: get: tags: - autonomy summary: Is autonomous action frozen description: 'Readable by any authenticated user in the tenant. Deliberately not behind a write permission: an analyst watching the queue stop moving needs to be able to find out why without asking an administrator.' operationId: read_kill_switch_api_v1_kill_switch_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/KillSwitchState' security: - HTTPBearer: [] /api/v1/kill-switch/engage: post: tags: - autonomy summary: Freeze autonomous closure and action dispatch for this tenant operationId: engage_api_v1_kill_switch_engage_post requestBody: content: application/json: schema: $ref: '#/components/schemas/EngageRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/KillSwitchState' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/kill-switch/release: post: tags: - autonomy summary: Resume autonomous closure and action dispatch operationId: release_api_v1_kill_switch_release_post requestBody: content: application/json: schema: $ref: '#/components/schemas/ReleaseRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/KillSwitchState' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/kill-switch/global/engage: post: tags: - autonomy summary: Freeze autonomous closure and action dispatch for every tenant description: 'Stop autonomous closure and dispatch across the whole deployment. One row, upserted. The conflict target is the expression index `aisoc_kill_switch_global_idx ON aisoc_kill_switch ((TRUE)) WHERE tenant_id IS NULL`, which is what keeps the global switch a singleton rather than a pile of rows a reader would have to reconcile at 3am.' operationId: engage_global_api_v1_kill_switch_global_engage_post requestBody: content: application/json: schema: $ref: '#/components/schemas/EngageRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/KillSwitchState' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/kill-switch/global/release: post: tags: - autonomy summary: Resume autonomous closure and action dispatch across every tenant description: 'Release the platform-wide stop. A tenant that engaged its own switch keeps it: the two scopes are independent and neither needs the other''s agreement, so releasing globally must not silently resume a tenant that asked to stay stopped.' operationId: release_global_api_v1_kill_switch_global_release_post requestBody: content: application/json: schema: $ref: '#/components/schemas/ReleaseRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/KillSwitchState' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/ledger/{run_id}/publish/preview: post: tags: - replay summary: Preview Publish description: Build the redacted snapshot + alias map for the pre-publish review. operationId: preview_publish_api_v1_ledger__run_id__publish_preview_post security: - HTTPBearer: [] parameters: - name: run_id in: path required: true schema: type: string format: uuid title: Run Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PublishPreview' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/ledger/{run_id}/publish: post: tags: - replay summary: Publish Replay description: Persist an immutable, redacted public snapshot and return its share URL. operationId: publish_replay_api_v1_ledger__run_id__publish_post security: - HTTPBearer: [] parameters: - name: run_id in: path required: true schema: type: string format: uuid title: Run Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PublishRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PublishResult' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/ledger/{run_id}/published: get: tags: - replay summary: List Published operationId: list_published_api_v1_ledger__run_id__published_get security: - HTTPBearer: [] parameters: - name: run_id in: path required: true schema: type: string format: uuid title: Run Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/PublishedSummary' title: Response List Published Api V1 Ledger Run Id Published Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/ledger/publish/{slug}: delete: tags: - replay summary: Unpublish Replay operationId: unpublish_replay_api_v1_ledger_publish__slug__delete security: - HTTPBearer: [] parameters: - name: slug in: path required: true schema: type: string title: Slug responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/r/{slug}: get: tags: - replay-public summary: Get Public Replay description: 'Public, unauthenticated read of a redacted replay snapshot. Uses a non-RLS session because ``published_replays`` is intentionally public-by-design (post-redaction, non-identifying). Increments a best-effort view counter (the only mutation the immutability trigger permits).' operationId: get_public_replay_api_v1_r__slug__get parameters: - name: slug in: path required: true schema: type: string title: Slug responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PublicReplay' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/access-review/kinds: get: tags: - access-review summary: The finding kinds this review can produce description: 'Published so the console can render a filter without hardcoding a copy. A second list in the frontend is how a kind ends up filterable on one surface and invisible on the other; this repository has that defect on record in the playbook editor''s step-type union.' operationId: list_kinds_api_v1_access_review_kinds_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response List Kinds Api V1 Access Review Kinds Get security: - HTTPBearer: [] /api/v1/access-review/summary: get: tags: - access-review summary: Review state and counts, with an honest empty state operationId: review_summary_api_v1_access_review_summary_get security: - HTTPBearer: [] parameters: - name: provider in: query required: false schema: anyOf: - type: string - type: 'null' title: Provider responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Review Summary Api V1 Access Review Summary Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/access-review/findings: get: tags: - access-review summary: Open access-review findings operationId: list_findings_api_v1_access_review_findings_get security: - HTTPBearer: [] parameters: - name: provider in: query required: false schema: anyOf: - type: string - type: 'null' title: Provider - name: kind in: query required: false schema: anyOf: - type: string - type: 'null' title: Kind - name: include_acknowledged in: query required: false schema: type: boolean default: false title: Include Acknowledged - name: limit in: query required: false schema: type: integer maximum: 500 minimum: 1 default: 100 title: Limit responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response List Findings Api V1 Access Review Findings Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/access-review/run: post: tags: - access-review summary: Recompute the review from current posture and usage description: 'Recompute. Reads only — no vendor object is touched by this call. Guarded by ``compliance:read`` rather than a write permission on purpose: the only thing it writes is this tenant''s own review rows, and requiring a write scope would put the review behind a permission an auditor is deliberately not given.' operationId: run_review_api_v1_access_review_run_post security: - HTTPBearer: [] parameters: - name: provider in: query required: true schema: type: string title: Provider responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Run Review Api V1 Access Review Run Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/access-review/findings/{finding_id}/acknowledge: post: tags: - access-review summary: Acknowledge a finding description: 'Record that a person decided this one is fine. A 404 rather than a 403 for a finding belonging to another tenant: a 403 would make this route an existence oracle over every tenant''s finding ids, and a genuine miss is byte-identical to a cross-tenant attempt from the caller''s side either way.' operationId: acknowledge_api_v1_access_review_findings__finding_id__acknowledge_post security: - HTTPBearer: [] parameters: - name: finding_id in: path required: true schema: type: string format: uuid title: Finding Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AcknowledgeRequest' responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Acknowledge Api V1 Access Review Findings Finding Id Acknowledge Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/identity/effective-permissions/providers: get: tags: - identity-effective-permissions summary: List supported effective-permissions providers description: 'Return ``{"providers": [{"name", "coverage"}, ...]}``. The UI calls this once on mount to render the provider switcher and to grey out scaffolded providers.' operationId: list_providers_api_v1_identity_effective_permissions_providers_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response List Providers Api V1 Identity Effective Permissions Providers Get security: - HTTPBearer: [] /api/v1/identity/{principal_id}/effective-permissions: get: tags: - identity-effective-permissions summary: Resolve effective permissions for a principal description: 'Resolve and return the effective-permissions envelope. Cache write is dispatched as a FastAPI ``BackgroundTask`` so the request returns as soon as the resolver finishes — the UI never waits on Neo4j.' operationId: get_effective_permissions_api_v1_identity__principal_id__effective_permissions_get security: - HTTPBearer: [] parameters: - name: principal_id in: path required: true schema: type: string title: Principal Id - name: provider in: query required: true schema: type: string description: Provider to resolve against (aws|azure|gcp|okta|gws). title: Provider description: Provider to resolve against (aws|azure|gcp|okta|gws). - name: snapshot_b64 in: query required: false schema: anyOf: - type: string - type: 'null' description: Optional base64-encoded JSON snapshot for dry-run resolution. Only honoured when AISOC_ALLOW_INLINE_SNAPSHOT=1 is set. title: Snapshot B64 description: Optional base64-encoded JSON snapshot for dry-run resolution. Only honoured when AISOC_ALLOW_INLINE_SNAPSHOT=1 is set. responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Get Effective Permissions Api V1 Identity Principal Id Effective Permissions Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/actions/email-decide: get: tags: - actions summary: Email Decide description: 'Verify a signed approval link and forward the decision. Unauthenticated by necessity — the recipient is reading email, not holding a session — so the token *is* the credential and every check that would otherwise come from a session has to come from the signature.' operationId: email_decide_api_v1_actions_email_decide_get parameters: - name: token in: query required: true schema: type: string description: Signed approval token title: Token description: Signed approval token responses: '200': description: Successful Response content: text/html: schema: type: string '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/costs/dashboard: get: tags: - costs summary: LLM spend, action counts, top-cost cases, and BYOK savings for the tenant description: "Return a deterministic snapshot of cost, activity, and BYOK savings.\n\ \nThe response covers the rolling ``window_days``-long window ending at\n\ ``now()`` (UTC) and includes:\n\n* **headline** — total spend / tokens / calls\ \ / runs and average\n cost-per-run across the window.\n* **daily_costs**\ \ — per-day buckets so the UI can plot a trend line.\n* **by_model** — spend,\ \ tokens, runs, and average latency per model\n with the imputed public-list\ \ cost for context.\n* **top_cases** — the most expensive ``case_id`` values,\ \ the closest\n proxy AiSOC has for \"top-cost playbook runs\" since each\ \ playbook\n run lives under a case (no separate ``playbook_runs`` table).\n\ * **action_counts** — ``audit_log`` action distribution so operators\n can\ \ see how much SOC activity their LLM spend is buying.\n* **byok_savings**\ \ — imputed delta between recorded cost and public\n list pricing. ``is_byok_active``\ \ reflects whether the live LLM\n provider is loopback / private (per ``/llm/status``).\n\ \nTenant scoping is enforced by ``TenantDBSession`` (Postgres RLS sets\n``app.current_tenant_id``)\ \ plus an explicit ``tenant_id`` predicate in\nthe underlying query, so the\ \ same payload is safe to render for any\nrole with ``reports:read``." operationId: get_cost_dashboard_api_v1_costs_dashboard_get security: - HTTPBearer: [] parameters: - name: window_days in: query required: false schema: type: integer maximum: 365 minimum: 1 description: Rolling window in days. Counts back from now() (UTC). Defaults to the last 30 days; capped at 365. default: 30 title: Window Days description: Rolling window in days. Counts back from now() (UTC). Defaults to the last 30 days; capped at 365. responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CostDashboard' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/push/public-key: get: tags: - push summary: Get Public Key description: 'Return the VAPID public key the PWA needs to subscribe. Public on purpose: the key is an application-server identifier, not a secret. Keeping it unauthenticated lets the service worker fetch it on install before the user has logged in.' operationId: get_public_key_api_v1_push_public_key_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Get Public Key Api V1 Push Public Key Get /api/v1/push/subscribe: post: tags: - push summary: Subscribe description: Register a PushSubscription for the authenticated user. operationId: subscribe_api_v1_push_subscribe_post requestBody: content: application/json: schema: additionalProperties: true type: object title: Body required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Subscribe Api V1 Push Subscribe Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/push/unsubscribe: post: tags: - push summary: Unsubscribe description: Remove a PushSubscription for the authenticated user. operationId: unsubscribe_api_v1_push_unsubscribe_post requestBody: content: application/json: schema: additionalProperties: true type: object title: Body required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Unsubscribe Api V1 Push Unsubscribe Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/push/test: post: tags: - push summary: Test Notify description: 'Send a test notification to the authenticated user''s devices. Useful for the PWA settings screen "send test" button.' operationId: test_notify_api_v1_push_test_post requestBody: content: application/json: schema: anyOf: - additionalProperties: true type: object - type: 'null' title: Body responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Test Notify Api V1 Push Test Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/oncall: get: tags: - responder - oncall summary: List Oncall description: 'List on-call status for everyone in the tenant. The PWA paging UI lights up everyone marked ``available`` first, then ``busy`` (so the next-best person is one tap away), and finally everyone else for context.' operationId: list_oncall_api_v1_oncall_get security: - HTTPBearer: [] parameters: - name: status in: query required: false schema: anyOf: - type: string - type: 'null' title: Status responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/OnCallListResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/oncall/me: get: tags: - responder - oncall summary: My Oncall operationId: my_oncall_api_v1_oncall_me_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/OnCallEntry' security: - HTTPBearer: [] put: tags: - responder - oncall summary: Update My Oncall description: 'Set my own on-call status. Use ``snoozed`` with ``snooze_minutes`` to defer paging temporarily; the PWA shows a countdown and the agent uses it to skip you for that window.' operationId: update_my_oncall_api_v1_oncall_me_put requestBody: content: application/json: schema: $ref: '#/components/schemas/OnCallUpdateRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/OnCallEntry' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/approvals: get: tags: - responder - approvals summary: List Approvals description: 'List approvals for the current tenant. Defaults to ``status=pending`` so the PWA inbox view is fast. Set ``mine=true`` to only show approvals routed specifically to the current user.' operationId: list_approvals_api_v1_approvals_get security: - HTTPBearer: [] 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: 200 minimum: 1 default: 25 title: Page Size - name: status in: query required: false schema: anyOf: - type: string - type: 'null' default: pending title: Status - name: mine in: query required: false schema: type: boolean default: false title: Mine - name: risk_level in: query required: false schema: anyOf: - type: string - type: 'null' title: Risk Level responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ApprovalListResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - responder - approvals summary: Create Approval description: 'Create a new approval request. The agents service calls this when it hits a high-risk step that requires human sign-off. We persist it, then fan-out a Web Push notification via the realtime service so the PWA buzzes the right on-call analyst.' operationId: create_approval_api_v1_approvals_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ApprovalCreateRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ApprovalResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/approvals/{approval_id}: get: tags: - responder - approvals summary: Get Approval operationId: get_approval_api_v1_approvals__approval_id__get security: - HTTPBearer: [] parameters: - name: approval_id in: path required: true schema: type: string format: uuid title: Approval Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ApprovalResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/approvals/{approval_id}/decide: post: tags: - responder - approvals summary: Decide Approval description: 'Approve or deny a pending approval. Idempotent only on the same decision: re-approving an already approved request returns the row unchanged; trying to flip an approved row to denied (or vice versa) is a 409.' operationId: decide_approval_api_v1_approvals__approval_id__decide_post security: - HTTPBearer: [] parameters: - name: approval_id in: path required: true schema: type: string format: uuid title: Approval Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ApprovalDecisionRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ApprovalResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/outbound-webhooks: get: tags: - integrations summary: List Webhooks operationId: list_webhooks_api_v1_outbound_webhooks_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/WebhookOut' type: array title: Response List Webhooks Api V1 Outbound Webhooks Get security: - HTTPBearer: [] post: tags: - integrations summary: Create Webhook description: 'Create a destination and mint its signing secret. The secret is returned **once**, here, and never again — it is stored vault-encrypted and the read projection carries only ``has_secret``. Returning it on every read would put a live credential in every console response and every browser cache that held one.' operationId: create_webhook_api_v1_outbound_webhooks_post requestBody: content: application/json: schema: $ref: '#/components/schemas/WebhookCreate' required: true responses: '201': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Create Webhook Api V1 Outbound Webhooks Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/outbound-webhooks/{webhook_id}: patch: tags: - integrations summary: Update Webhook operationId: update_webhook_api_v1_outbound_webhooks__webhook_id__patch security: - HTTPBearer: [] parameters: - name: webhook_id in: path required: true schema: type: string format: uuid title: Webhook Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/WebhookUpdate' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/WebhookOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - integrations summary: Delete Webhook operationId: delete_webhook_api_v1_outbound_webhooks__webhook_id__delete security: - HTTPBearer: [] parameters: - name: webhook_id in: path required: true schema: type: string format: uuid title: Webhook Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/outbound-webhooks/{webhook_id}/rotate-secret: post: tags: - integrations summary: Rotate Secret operationId: rotate_secret_api_v1_outbound_webhooks__webhook_id__rotate_secret_post security: - HTTPBearer: [] parameters: - name: webhook_id in: path required: true schema: type: string format: uuid title: Webhook Id responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Rotate Secret Api V1 Outbound Webhooks Webhook Id Rotate Secret Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/outbound-webhooks/deliveries: get: tags: - integrations summary: List Deliveries operationId: list_deliveries_api_v1_outbound_webhooks_deliveries_get security: - HTTPBearer: [] parameters: - name: status in: query required: false schema: anyOf: - type: string pattern: ^(pending|delivered|failed|dead)$ - type: 'null' title: Status - name: limit in: query required: false schema: type: integer maximum: 500 minimum: 1 default: 100 title: Limit responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/DeliveryOut' title: Response List Deliveries Api V1 Outbound Webhooks Deliveries Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/outbound-webhooks/dead-letters: get: tags: - integrations summary: List Dead Letters description: 'Deliveries that exhausted their retries or were refused outright. A named route rather than ``?status=dead`` on the list above, because this is the one an operator is told to check and a filter nobody applies is a filter nobody sees.' operationId: list_dead_letters_api_v1_outbound_webhooks_dead_letters_get security: - HTTPBearer: [] parameters: - name: limit in: query required: false schema: type: integer maximum: 500 minimum: 1 default: 100 title: Limit responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/DeliveryOut' title: Response List Dead Letters Api V1 Outbound Webhooks Dead Letters Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/outbound-webhooks/deliveries/{delivery_id}/replay: post: tags: - integrations summary: Replay Delivery description: 'Send a dead letter again, with the body the first attempt sent. Synchronous and single-attempt: an operator replaying by hand has just fixed something and wants to know now whether it worked, not in thirty seconds. The attempt counter keeps climbing, so a replay cannot be used to loop past the ladder.' operationId: replay_delivery_api_v1_outbound_webhooks_deliveries__delivery_id__replay_post security: - HTTPBearer: [] parameters: - name: delivery_id in: path required: true schema: type: string format: uuid title: Delivery Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/DeliveryOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/passkeys/register/begin: post: tags: - responder - passkeys summary: Passkey Register Begin description: 'Start passkey enrollment for the currently logged-in user. Returns a JSON payload directly suitable for ``navigator.credentials.create({ publicKey: })`` after the standard Base64URL-decoding of the binary fields.' operationId: passkey_register_begin_api_v1_passkeys_register_begin_post requestBody: content: application/json: schema: $ref: '#/components/schemas/RegisterBeginRequest' required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Passkey Register Begin Api V1 Passkeys Register Begin Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/passkeys/register/finish: post: tags: - responder - passkeys summary: Passkey Register Finish description: Verify the attestation response and persist the new credential. operationId: passkey_register_finish_api_v1_passkeys_register_finish_post requestBody: content: application/json: schema: $ref: '#/components/schemas/FinishRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PasskeyCredentialOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/passkeys/authenticate/begin: post: tags: - responder - passkeys summary: Passkey Authenticate Begin description: 'Start a passkey-based login. If ``email`` is provided we constrain ``allowCredentials`` so only that user''s keys are tried. If omitted we go fully discoverable (the browser picks the right credential from its store).' operationId: passkey_authenticate_begin_api_v1_passkeys_authenticate_begin_post requestBody: content: application/json: schema: $ref: '#/components/schemas/AuthenticateBeginRequest' required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Passkey Authenticate Begin Api V1 Passkeys Authenticate Begin Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/passkeys/authenticate/finish: post: tags: - responder - passkeys summary: Passkey Authenticate Finish description: Verify a passkey assertion and mint JWTs. operationId: passkey_authenticate_finish_api_v1_passkeys_authenticate_finish_post requestBody: content: application/json: schema: $ref: '#/components/schemas/FinishRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AuthenticateFinishResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/passkeys/credentials: get: tags: - responder - passkeys summary: List My Credentials description: List the current user's active passkeys. operationId: list_my_credentials_api_v1_passkeys_credentials_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CredentialsListResponse' security: - HTTPBearer: [] /api/v1/passkeys/credentials/{credential_id}: delete: tags: - responder - passkeys summary: Revoke Credential description: Revoke (soft-delete) one of the current user's passkeys. operationId: revoke_credential_api_v1_passkeys_credentials__credential_id__delete security: - HTTPBearer: [] parameters: - name: credential_id in: path required: true schema: type: string format: uuid title: Credential Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/live-actions: get: tags: - actions summary: List every registered live action operationId: discover_api_v1_live_actions_get security: - HTTPBearer: [] parameters: - name: vendor_id in: query required: false schema: anyOf: - type: string - type: 'null' title: Vendor Id - name: capability in: query required: false schema: anyOf: - type: string - type: 'null' title: Capability responses: '200': description: Successful Response content: application/json: schema: title: Response Discover Api V1 Live Actions Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/live-actions/by-capability/{capability}: get: tags: - actions summary: Vendors implementing a capability operationId: vendors_for_capability_api_v1_live_actions_by_capability__capability__get security: - HTTPBearer: [] parameters: - name: capability in: path required: true schema: type: string title: Capability responses: '200': description: Successful Response content: application/json: schema: title: Response Vendors For Capability Api V1 Live Actions By Capability Capability Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/live-actions/by-vendor/{vendor_id}: get: tags: - actions summary: Capabilities a vendor supports operationId: capabilities_for_vendor_api_v1_live_actions_by_vendor__vendor_id__get security: - HTTPBearer: [] parameters: - name: vendor_id in: path required: true schema: type: string title: Vendor Id responses: '200': description: Successful Response content: application/json: schema: title: Response Capabilities For Vendor Api V1 Live Actions By Vendor Vendor Id Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/live-actions/dry-run: post: tags: - actions summary: Preview a live action without touching a vendor description: 'Proxy a dry run, with the tenant taken from the session. Only ``/dry-run`` is proxied, never ``/dispatch``. Upstream, ``/dry-run`` forces ``dry_run=true`` server-side regardless of the body, so a client cannot talk its way into a live vendor call through this route.' operationId: dry_run_api_v1_live_actions_dry_run_post requestBody: content: application/json: schema: additionalProperties: true type: object title: Body required: true responses: '200': description: Successful Response content: application/json: schema: title: Response Dry Run Api V1 Live Actions Dry Run Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/saved-views: get: tags: - saved-views summary: List Saved Views description: 'Return the caller''s saved views for one list page. Sorted by ``is_default DESC, updated_at DESC`` so the default preset always lands first, with the rest ordered by recency. The UI uses this ordering directly to render the saved-views menu.' operationId: list_saved_views_api_v1_saved_views_get security: - HTTPBearer: [] parameters: - name: view_type in: query required: true schema: type: string description: 'Which list page to return presets for. One of: alerts, cases, investigations, playbooks.' title: View Type description: 'Which list page to return presets for. One of: alerts, cases, investigations, playbooks.' responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/SavedViewModel' title: Response List Saved Views Api V1 Saved Views Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - saved-views summary: Create Saved View description: 'Create a new saved view. If ``is_default=True`` is passed, any existing default for the same ``(tenant, user, view_type)`` is demoted *first* so the partial unique index in 037 doesn''t trip. We do the demote + insert in a single transaction so a concurrent ``POST`` from the same user can''t leave the table without a default.' operationId: create_saved_view_api_v1_saved_views_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateSavedViewRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SavedViewModel' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/saved-views/{view_id}: patch: tags: - saved-views summary: Update Saved View description: 'Patch a saved view in place. Sending only ``is_default=True`` is the canonical "make this my default" path. We demote the existing default first inside the same transaction.' operationId: update_saved_view_api_v1_saved_views__view_id__patch security: - HTTPBearer: [] parameters: - name: view_id in: path required: true schema: type: string title: View Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateSavedViewRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SavedViewModel' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - saved-views summary: Delete Saved View description: Delete a saved view owned by the caller. operationId: delete_saved_view_api_v1_saved_views__view_id__delete security: - HTTPBearer: [] parameters: - name: view_id in: path required: true schema: type: string title: View Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/scim-tokens: get: tags: - scim summary: List Scim Tokens description: List this tenant's SCIM credentials. Secrets are never returned. operationId: list_scim_tokens_api_v1_scim_tokens_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/ScimTokenOut' type: array title: Response List Scim Tokens Api V1 Scim Tokens Get security: - HTTPBearer: [] post: tags: - scim summary: Create Scim Token description: Mint a SCIM credential for this tenant. operationId: create_scim_token_api_v1_scim_tokens_post requestBody: content: application/json: schema: $ref: '#/components/schemas/CreateScimTokenRequest' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CreatedScimTokenOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/scim-tokens/{token_id}/rotate: post: tags: - scim summary: Rotate Scim Token description: Replace a credential, leaving the old one working for a grace window. operationId: rotate_scim_token_api_v1_scim_tokens__token_id__rotate_post security: - HTTPBearer: [] parameters: - name: token_id in: path required: true schema: type: string format: uuid title: Token Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RotateScimTokenRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CreatedScimTokenOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/scim-tokens/{token_id}: delete: tags: - scim summary: Revoke Scim Token description: 'Revoke a credential immediately. The row stays, because ``aisoc_scim_users.token_id`` references it and the audit trail answers "which integration provisioned this principal" through it.' operationId: revoke_scim_token_api_v1_scim_tokens__token_id__delete security: - HTTPBearer: [] parameters: - name: token_id in: path required: true schema: type: string format: uuid title: Token Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/verification-policies: get: tags: - settings summary: List Policies description: Every class this tenant has opted into. Empty means none. operationId: list_policies_api_v1_verification_policies_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/VerificationPolicyOut' type: array title: Response List Policies Api V1 Verification Policies Get security: - HTTPBearer: [] put: tags: - settings summary: Enable Class description: 'Allow automatic verification prompts for one alert class. Idempotent: re-enabling an already-enabled class re-stamps who asked for it and when, which is the record a review after a complaint needs.' operationId: enable_class_api_v1_verification_policies_put requestBody: content: application/json: schema: $ref: '#/components/schemas/VerificationPolicyIn' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/VerificationPolicyOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/verification-policies/{alert_class}: delete: tags: - settings summary: Disable Class description: 'Stop automatic prompts for one class. Answers 204 whether or not a row existed. A tenant revoking a grant wants it gone; telling them it was already gone is not useful and a 404 here would read as "the revocation failed".' operationId: disable_class_api_v1_verification_policies__alert_class__delete security: - HTTPBearer: [] parameters: - name: alert_class in: path required: true schema: type: string title: Alert Class responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/sso-connections: get: tags: - sso summary: List Sso Connections description: Every SSO connection this tenant has configured. operationId: list_sso_connections_api_v1_sso_connections_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/SsoConnectionOut' type: array title: Response List Sso Connections Api V1 Sso Connections Get security: - HTTPBearer: [] post: tags: - sso summary: Create Sso Connection description: 'Configure an identity provider for this tenant. Until one of these exists, every SAML and OIDC sign-in answers 403 — `resolve_connection` finds no row and refuses rather than guessing a tenant from the assertion.' operationId: create_sso_connection_api_v1_sso_connections_post requestBody: content: application/json: schema: $ref: '#/components/schemas/SsoConnectionIn' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SsoConnectionOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/sso-connections/{connection_id}: patch: tags: - sso summary: Update Sso Connection description: 'Replace a connection''s configuration. The tenant is not settable: moving a connection between tenants would silently re-home every user who signs in through it.' operationId: update_sso_connection_api_v1_sso_connections__connection_id__patch security: - HTTPBearer: [] parameters: - name: connection_id in: path required: true schema: type: string format: uuid title: Connection Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SsoConnectionIn' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SsoConnectionOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - sso summary: Delete Sso Connection description: Remove a connection. Sign-ins through that issuer start answering 403 again. operationId: delete_sso_connection_api_v1_sso_connections__connection_id__delete security: - HTTPBearer: [] parameters: - name: connection_id in: path required: true schema: type: string format: uuid title: Connection Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/assets: get: tags: - assets summary: List Assets operationId: list_assets_api_v1_assets_get security: - HTTPBearer: [] parameters: - name: asset_type in: query required: false schema: anyOf: - type: string - type: 'null' title: Asset Type - name: criticality in: query required: false schema: anyOf: - type: string - type: 'null' title: Criticality - name: limit in: query required: false schema: type: integer maximum: 500 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: array items: $ref: '#/components/schemas/AssetOut' title: Response List Assets Api V1 Assets Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - assets summary: Create Asset operationId: create_asset_api_v1_assets_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AssetCreate' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AssetOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/assets/vulnerabilities: get: tags: - assets summary: List Vulnerabilities operationId: list_vulnerabilities_api_v1_assets_vulnerabilities_get security: - HTTPBearer: [] parameters: - name: severity in: query required: false schema: anyOf: - type: string - type: 'null' title: Severity - name: is_exploited in: query required: false schema: anyOf: - type: boolean - type: 'null' title: Is Exploited - name: limit in: query required: false schema: type: integer maximum: 500 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: array items: $ref: '#/components/schemas/VulnerabilityOut' title: Response List Vulnerabilities Api V1 Assets Vulnerabilities Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - assets summary: Create Vulnerability operationId: create_vulnerability_api_v1_assets_vulnerabilities_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/VulnerabilityCreate' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/VulnerabilityOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/assets/{asset_id}: get: tags: - assets summary: Get Asset operationId: get_asset_api_v1_assets__asset_id__get security: - HTTPBearer: [] parameters: - name: asset_id in: path required: true schema: type: string format: uuid title: Asset Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AssetOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' patch: tags: - assets summary: Update Asset operationId: update_asset_api_v1_assets__asset_id__patch security: - HTTPBearer: [] parameters: - name: asset_id in: path required: true schema: type: string format: uuid title: Asset Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AssetUpdate' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AssetOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - assets summary: Delete Asset operationId: delete_asset_api_v1_assets__asset_id__delete security: - HTTPBearer: [] parameters: - name: asset_id in: path required: true schema: type: string format: uuid title: Asset Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/assets/{asset_id}/vulnerabilities: get: tags: - assets summary: List Asset Vulnerabilities operationId: list_asset_vulnerabilities_api_v1_assets__asset_id__vulnerabilities_get security: - HTTPBearer: [] parameters: - name: asset_id in: path required: true schema: type: string format: uuid title: Asset Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/VulnerabilityOut' title: Response List Asset Vulnerabilities Api V1 Assets Asset Id Vulnerabilities Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mssp/children: get: tags: - mssp summary: List Child Tenants description: Return all child tenants of the current parent tenant. operationId: list_child_tenants_api_v1_mssp_children_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/ChildTenantOut' type: array title: Response List Child Tenants Api V1 Mssp Children Get security: - HTTPBearer: [] /api/v1/mssp/children/{child_id}/onboard: post: tags: - mssp summary: Onboard Child Tenant description: 'Link an existing tenant as a child, if that tenant invited the caller. Adoption previously required nothing but an authenticated session and a tenant UUID: the only check was a 409 when the target already had a parent, so **every standalone tenant on the deployment was adoptable by any user**, and adoption is what makes the child-scoped write routes below accept you. That is the root of the escalation ``_require_own_child`` describes — closing those routes alone would not have helped, because an attacker could simply adopt the victim first. Consent is now required and comes from the child''s own side: an admin of the tenant being adopted sets ``settings["mssp_parent_invite"]`` to the parent''s UUID through ``PATCH /api/v1/tenants/me/settings``. The invite is single-use — it is cleared here — so a stale value cannot re-adopt a tenant that later left.' operationId: onboard_child_tenant_api_v1_mssp_children__child_id__onboard_post security: - HTTPBearer: [] parameters: - name: child_id in: path required: true schema: type: string format: uuid title: Child Id responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: type: string title: Response Onboard Child Tenant Api V1 Mssp Children Child Id Onboard Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mssp/notes: get: tags: - mssp summary: List Notes operationId: list_notes_api_v1_mssp_notes_get security: - HTTPBearer: [] parameters: - name: child_id in: query required: false schema: anyOf: - type: string format: uuid - type: 'null' title: Child Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/TenantNoteOut' title: Response List Notes Api V1 Mssp Notes Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - mssp summary: Create Note operationId: create_note_api_v1_mssp_notes_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TenantNoteCreate' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TenantNoteOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mssp/delegations: get: tags: - mssp summary: List Delegations operationId: list_delegations_api_v1_mssp_delegations_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/DelegationOut' type: array title: Response List Delegations Api V1 Mssp Delegations Get security: - HTTPBearer: [] post: tags: - mssp summary: Create Delegation operationId: create_delegation_api_v1_mssp_delegations_post requestBody: content: application/json: schema: $ref: '#/components/schemas/DelegationCreate' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/DelegationOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/mssp/delegations/{delegation_id}: delete: tags: - mssp summary: Revoke Delegation operationId: revoke_delegation_api_v1_mssp_delegations__delegation_id__delete security: - HTTPBearer: [] parameters: - name: delegation_id in: path required: true schema: type: string format: uuid title: Delegation Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mssp/metrics: get: tags: - mssp summary: List Metrics description: Return the latest metrics snapshot for every child tenant. operationId: list_metrics_api_v1_mssp_metrics_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/MetricsOut' type: array title: Response List Metrics Api V1 Mssp Metrics Get security: - HTTPBearer: [] /api/v1/mssp/rule-packs: get: tags: - mssp summary: List Rule Packs description: List all rule packs owned by the current parent tenant. operationId: list_rule_packs_api_v1_mssp_rule_packs_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/RulePackOut' type: array title: Response List Rule Packs Api V1 Mssp Rule Packs Get security: - HTTPBearer: [] post: tags: - mssp summary: Create Rule Pack description: Create a new rule pack (parent tenant only). operationId: create_rule_pack_api_v1_mssp_rule_packs_post requestBody: content: application/json: schema: $ref: '#/components/schemas/RulePackCreate' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RulePackOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/mssp/rule-packs/{pack_id}: get: tags: - mssp summary: Get Rule Pack operationId: get_rule_pack_api_v1_mssp_rule_packs__pack_id__get security: - HTTPBearer: [] parameters: - name: pack_id in: path required: true schema: type: string format: uuid title: Pack Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RulePackOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' put: tags: - mssp summary: Update Rule Pack operationId: update_rule_pack_api_v1_mssp_rule_packs__pack_id__put security: - HTTPBearer: [] parameters: - name: pack_id in: path required: true schema: type: string format: uuid title: Pack Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RulePackUpdate' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RulePackOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - mssp summary: Delete Rule Pack operationId: delete_rule_pack_api_v1_mssp_rule_packs__pack_id__delete security: - HTTPBearer: [] parameters: - name: pack_id in: path required: true schema: type: string format: uuid title: Pack Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mssp/rule-packs/{pack_id}/rules: post: tags: - mssp summary: Add Rule To Pack operationId: add_rule_to_pack_api_v1_mssp_rule_packs__pack_id__rules_post security: - HTTPBearer: [] parameters: - name: pack_id in: path required: true schema: type: string format: uuid title: Pack Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RulePackRuleAdd' responses: '201': description: Successful Response content: application/json: schema: type: object additionalProperties: type: string title: Response Add Rule To Pack Api V1 Mssp Rule Packs Pack Id Rules Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mssp/rule-packs/{pack_id}/rules/{rule_id}: delete: tags: - mssp summary: Remove Rule From Pack operationId: remove_rule_from_pack_api_v1_mssp_rule_packs__pack_id__rules__rule_id__delete security: - HTTPBearer: [] parameters: - name: pack_id in: path required: true schema: type: string format: uuid title: Pack Id - name: rule_id in: path required: true schema: type: string format: uuid title: Rule Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mssp/rule-packs/{pack_id}/assign: post: tags: - mssp summary: Assign Pack To Child operationId: assign_pack_to_child_api_v1_mssp_rule_packs__pack_id__assign_post security: - HTTPBearer: [] parameters: - name: pack_id in: path required: true schema: type: string format: uuid title: Pack Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PackAssignmentCreate' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PackAssignmentOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mssp/overrides: post: tags: - mssp summary: Create Rule Override operationId: create_rule_override_api_v1_mssp_overrides_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RuleOverrideCreate' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RuleOverrideOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' get: tags: - mssp summary: List Overrides operationId: list_overrides_api_v1_mssp_overrides_get security: - HTTPBearer: [] parameters: - name: child_id in: query required: false schema: anyOf: - type: string format: uuid - type: 'null' title: Child Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/RuleOverrideOut' title: Response List Overrides Api V1 Mssp Overrides Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mssp/overrides/{override_id}: delete: tags: - mssp summary: Delete Override operationId: delete_override_api_v1_mssp_overrides__override_id__delete security: - HTTPBearer: [] parameters: - name: override_id in: path required: true schema: type: string format: uuid title: Override Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mssp/children/{child_id}/effective-rules: get: tags: - mssp summary: List Effective Rules For Child description: Preview the resolved rule set for a child tenant (parent-only). operationId: list_effective_rules_for_child_api_v1_mssp_children__child_id__effective_rules_get security: - HTTPBearer: [] parameters: - name: child_id in: path required: true schema: type: string format: uuid title: Child Id - name: category in: query required: false schema: anyOf: - type: string - type: 'null' title: Category - name: rule_language in: query required: false schema: anyOf: - type: string - type: 'null' title: Rule Language responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/EffectiveRuleOut' title: Response List Effective Rules For Child Api V1 Mssp Children Child Id Effective Rules Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mssp/children/{child_id}/effective-rules/count: get: tags: - mssp summary: Count Effective Rules For Child description: Return source-breakdown counts for a child tenant's effective ruleset. operationId: count_effective_rules_for_child_api_v1_mssp_children__child_id__effective_rules_count_get security: - HTTPBearer: [] parameters: - name: child_id in: path required: true schema: type: string format: uuid title: Child Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/EffectiveRuleCountOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mssp/portfolio: get: tags: - mssp summary: Get Portfolio description: 'Managed portfolio: per-tenant posture plus derived totals. Every figure is counted from that tenant''s own rows. A portfolio with no tenants returns zeros and an empty list.' operationId: get_portfolio_api_v1_mssp_portfolio_get security: - HTTPBearer: [] parameters: - name: tenant_id in: query required: false schema: anyOf: - type: array items: type: string format: uuid - type: 'null' description: Restrict to these tenants; ignored if outside the portfolio. title: Tenant Id description: Restrict to these tenants; ignored if outside the portfolio. responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PortfolioOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mssp/portfolio/alerts: get: tags: - mssp summary: List Portfolio Alerts description: Open alerts across the portfolio, newest first. operationId: list_portfolio_alerts_api_v1_mssp_portfolio_alerts_get security: - HTTPBearer: [] parameters: - name: severity in: query required: false schema: anyOf: - type: string - type: 'null' description: critical | high | medium | low | info title: Severity description: critical | high | medium | low | info - name: status in: query required: false schema: anyOf: - type: string - type: 'null' title: Status - name: include_synthetic in: query required: false schema: type: boolean description: Include seeded demo rows, labelled as such. default: false title: Include Synthetic description: Include seeded demo rows, labelled as such. - name: limit in: query required: false schema: type: integer maximum: 500 minimum: 1 default: 50 title: Limit - name: tenant_id in: query required: false schema: anyOf: - type: array items: type: string format: uuid - type: 'null' title: Tenant Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/PortfolioAlertOut' title: Response List Portfolio Alerts Api V1 Mssp Portfolio Alerts Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mssp/overview: get: tags: - mssp summary: Mssp Overview description: Portfolio totals for the operator dashboard. operationId: mssp_overview_api_v1_mssp_overview_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PortfolioSummaryOut' security: - HTTPBearer: [] /api/v1/mssp/tenants: get: tags: - mssp summary: List Managed Tenants description: Managed tenants with their measured posture. operationId: list_managed_tenants_api_v1_mssp_tenants_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/PortfolioTenantOut' type: array title: Response List Managed Tenants Api V1 Mssp Tenants Get security: - HTTPBearer: [] /api/v1/mssp/incidents: get: tags: - mssp summary: List Cross Tenant Incidents description: Open alerts across the portfolio. operationId: list_cross_tenant_incidents_api_v1_mssp_incidents_get security: - HTTPBearer: [] parameters: - name: severity in: query required: false schema: anyOf: - type: string - type: 'null' description: critical | high | medium | low | info title: Severity description: critical | high | medium | low | info - name: limit in: query required: false schema: type: integer maximum: 500 minimum: 1 default: 50 title: Limit responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/PortfolioAlertOut' title: Response List Cross Tenant Incidents Api V1 Mssp Incidents Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mssp/organizations: post: tags: - mssp summary: Create Organization description: 'Create an operator organisation around the caller''s own tenant. The creator becomes its owner. The home tenant is taken from the caller''s session rather than the body, so nobody can found an organisation on top of somebody else''s tenant.' operationId: create_organization_api_v1_mssp_organizations_post requestBody: content: application/json: schema: $ref: '#/components/schemas/OrganizationCreate' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/OrganizationOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/mssp/organizations/current: get: tags: - mssp summary: Get Current Organization operationId: get_current_organization_api_v1_mssp_organizations_current_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/OrganizationOut' security: - HTTPBearer: [] /api/v1/mssp/organizations/current/tenants: post: tags: - mssp summary: Add Tenants To Portfolio description: 'Bring tenants under management, with each tenant''s consent. A tenant must first invite this organisation by setting `settings["mssp_parent_invite"]` to the managing tenant''s id through `PATCH /api/v1/tenants/me/settings`. The invite is single-use. A tenant already managed by another organisation is rejected rather than reassigned.' operationId: add_tenants_to_portfolio_api_v1_mssp_organizations_current_tenants_post requestBody: content: application/json: schema: $ref: '#/components/schemas/TenantGrant' required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Add Tenants To Portfolio Api V1 Mssp Organizations Current Tenants Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/mssp/organizations/current/tenants/{tenant_id}: delete: tags: - mssp summary: Remove Tenant From Portfolio description: 'Release a tenant from management. Deletes the portfolio link only. The tenant and all of its data survive as a standalone tenant — offboarding a customer from a provider is not the same request as erasing them, and conflating the two would make this button destructive in a way its label does not say.' operationId: remove_tenant_from_portfolio_api_v1_mssp_organizations_current_tenants__tenant_id__delete security: - HTTPBearer: [] parameters: - name: tenant_id in: path required: true schema: type: string format: uuid title: Tenant Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/mssp/organizations/current/members: get: tags: - mssp summary: List Members operationId: list_members_api_v1_mssp_organizations_current_members_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/MemberOut' type: array title: Response List Members Api V1 Mssp Organizations Current Members Get security: - HTTPBearer: [] put: tags: - mssp summary: Upsert Member operationId: upsert_member_api_v1_mssp_organizations_current_members_put requestBody: content: application/json: schema: $ref: '#/components/schemas/MemberUpsert' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MemberOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/mssp/organizations/current/members/{user_id}/tenants: put: tags: - mssp summary: Set Member Tenant Grants description: 'Replace which portfolio tenants one member may reach. Tenants outside the portfolio are refused here *and* would be refused by the database: `organization_member_tenants` has a composite foreign key onto `organization_tenants`, so a grant cannot name a tenant the organisation does not manage even if this check were removed.' operationId: set_member_tenant_grants_api_v1_mssp_organizations_current_members__user_id__tenants_put security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string format: uuid title: User Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TenantGrant' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MemberOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/insider-threat/profiles: get: tags: - insider-threat summary: List Profiles operationId: list_profiles_api_v1_insider_threat_profiles_get security: - HTTPBearer: [] parameters: - name: risk_tier in: query required: false schema: anyOf: - type: string - type: 'null' title: Risk Tier - name: watchlisted_only in: query required: false schema: type: boolean default: false title: Watchlisted Only - name: limit in: query required: false schema: type: integer maximum: 500 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: array items: $ref: '#/components/schemas/RiskProfileOut' title: Response List Profiles Api V1 Insider Threat Profiles Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/insider-threat/profiles/{profile_id}: get: tags: - insider-threat summary: Get Profile operationId: get_profile_api_v1_insider_threat_profiles__profile_id__get security: - HTTPBearer: [] parameters: - name: profile_id in: path required: true schema: type: string format: uuid title: Profile Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RiskProfileOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/insider-threat/profiles/{profile_id}/watchlist: patch: tags: - insider-threat summary: Update Watchlist operationId: update_watchlist_api_v1_insider_threat_profiles__profile_id__watchlist_patch security: - HTTPBearer: [] parameters: - name: profile_id in: path required: true schema: type: string format: uuid title: Profile Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/WatchlistUpdate' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RiskProfileOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/insider-threat/indicators: get: tags: - insider-threat summary: List Indicators operationId: list_indicators_api_v1_insider_threat_indicators_get security: - HTTPBearer: [] parameters: - name: severity in: query required: false schema: anyOf: - type: string - type: 'null' title: Severity - name: indicator_type in: query required: false schema: anyOf: - type: string - type: 'null' title: Indicator Type - name: limit in: query required: false schema: type: integer maximum: 500 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: array items: $ref: '#/components/schemas/IndicatorOut' title: Response List Indicators Api V1 Insider Threat Indicators Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - insider-threat summary: Create Indicator operationId: create_indicator_api_v1_insider_threat_indicators_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/IndicatorCreate' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/IndicatorOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/insider-threat/indicators/{indicator_id}/acknowledge: post: tags: - insider-threat summary: Acknowledge Indicator operationId: acknowledge_indicator_api_v1_insider_threat_indicators__indicator_id__acknowledge_post security: - HTTPBearer: [] parameters: - name: indicator_id in: path required: true schema: type: string format: uuid title: Indicator Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/IndicatorOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/insider-threat/peer-groups: get: tags: - insider-threat summary: List Peer Groups operationId: list_peer_groups_api_v1_insider_threat_peer_groups_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/PeerGroupOut' type: array title: Response List Peer Groups Api V1 Insider Threat Peer Groups Get security: - HTTPBearer: [] post: tags: - insider-threat summary: Create Peer Group operationId: create_peer_group_api_v1_insider_threat_peer_groups_post requestBody: content: application/json: schema: $ref: '#/components/schemas/PeerGroupCreate' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PeerGroupOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/remediation/config: get: tags: - remediation summary: Get Maturity Config operationId: get_maturity_config_api_v1_remediation_config_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MaturityOut' security: - HTTPBearer: [] put: tags: - remediation summary: Update Maturity Config operationId: update_maturity_config_api_v1_remediation_config_put requestBody: content: application/json: schema: $ref: '#/components/schemas/MaturityConfig' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MaturityOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/remediation/gate-log: get: tags: - remediation summary: List Gate Log operationId: list_gate_log_api_v1_remediation_gate_log_get security: - HTTPBearer: [] parameters: - name: decision in: query required: false schema: anyOf: - type: string - type: 'null' title: Decision - name: limit in: query required: false schema: type: integer maximum: 500 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: array items: $ref: '#/components/schemas/GateLogOut' title: Response List Gate Log Api V1 Remediation Gate Log Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/remediation/whitelist: get: tags: - remediation summary: List Whitelist operationId: list_whitelist_api_v1_remediation_whitelist_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/WhitelistOut' type: array title: Response List Whitelist Api V1 Remediation Whitelist Get security: - HTTPBearer: [] post: tags: - remediation summary: Add To Whitelist operationId: add_to_whitelist_api_v1_remediation_whitelist_post requestBody: content: application/json: schema: $ref: '#/components/schemas/WhitelistCreate' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/WhitelistOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/remediation/whitelist/{entry_id}: delete: tags: - remediation summary: Remove From Whitelist operationId: remove_from_whitelist_api_v1_remediation_whitelist__entry_id__delete security: - HTTPBearer: [] parameters: - name: entry_id in: path required: true schema: type: string format: uuid title: Entry Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/data-lifecycle/retention: get: tags: - data_lifecycle summary: Get Retention operationId: get_retention_api_v1_data_lifecycle_retention_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RetentionOut' security: - HTTPBearer: [] put: tags: - data_lifecycle summary: Put Retention operationId: put_retention_api_v1_data_lifecycle_retention_put requestBody: content: application/json: schema: $ref: '#/components/schemas/RetentionUpdate' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RetentionOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/data-lifecycle/parsers: get: tags: - data_lifecycle summary: List Parsers operationId: list_parsers_api_v1_data_lifecycle_parsers_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/ParserOut' type: array title: Response List Parsers Api V1 Data Lifecycle Parsers Get security: - HTTPBearer: [] post: tags: - data_lifecycle summary: Create Parser operationId: create_parser_api_v1_data_lifecycle_parsers_post requestBody: content: application/json: schema: $ref: '#/components/schemas/ParserIn' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ParserOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/data-lifecycle/parsers/test: post: tags: - data_lifecycle summary: Test Parser description: 'Dry-run a parser pipeline against a sample event — validate + preview the reshaped event without persisting anything (W5.2/W5.3).' operationId: test_parser_api_v1_data_lifecycle_parsers_test_post requestBody: content: application/json: schema: $ref: '#/components/schemas/ParserTestIn' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ParserTestOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/data-lifecycle/parsers/{parser_id}: delete: tags: - data_lifecycle summary: Delete Parser operationId: delete_parser_api_v1_data_lifecycle_parsers__parser_id__delete security: - HTTPBearer: [] parameters: - name: parser_id in: path required: true schema: type: string format: uuid title: Parser Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/feedback/alert-override: post: tags: - feedback summary: Submit Alert Override description: 'Record an analyst verdict correction on an alert and surface retroactive re-disposition candidates.' operationId: submit_alert_override_api_v1_feedback_alert_override_post requestBody: content: application/json: schema: $ref: '#/components/schemas/AlertOverrideRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AlertOverrideResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/feedback/redisposition/apply: post: tags: - feedback summary: Apply Redisposition Endpoint description: 'Bulk-update the disposition on past alerts the analyst confirmed. Requires the confirmation token from the preview; a stale/tampered set or a batch over the cap is rejected rather than silently flipping history.' operationId: apply_redisposition_endpoint_api_v1_feedback_redisposition_apply_post requestBody: content: application/json: schema: $ref: '#/components/schemas/RedispositionApplyRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RedispositionApplyResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/feedback/overrides: get: tags: - feedback summary: List Overrides Endpoint description: List analyst-override entries the agent has 'learned' from. operationId: list_overrides_endpoint_api_v1_feedback_overrides_get security: - HTTPBearer: [] parameters: - name: limit in: query required: false schema: type: integer default: 100 title: Limit responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/OverrideEntryModel' title: Response List Overrides Endpoint Api V1 Feedback Overrides Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/feedback/summary: get: tags: - feedback summary: Get Override Summary description: Return a summary of analyst overrides for this tenant. operationId: get_override_summary_api_v1_feedback_summary_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/OverrideSummaryResponse' security: - HTTPBearer: [] /api/v1/feedback/context-statements: get: tags: - feedback summary: Get Context Statements description: 'Unexpired organisation memory for a tenant. Dual-mode for the same reason ``/alerts/{id}/source-writeback`` is: the consumer is the agents service''s triage worker, which has no session, and the API owns the tenant-scoped database session. A session caller''s ``tenant_id`` is **intersected** with the session rather than ignored. Ignoring it is safe but dishonest — a caller who names another tenant gets this tenant''s memory back under that tenant''s name, which is the "silently returns the wrong data" shape rather than a refusal. ``scoped_tenant_or_403`` returns the caller''s own tenant when the parameter is absent, honours it when it matches, and 403s otherwise.' operationId: get_context_statements_api_v1_feedback_context_statements_get security: - HTTPBearer: [] parameters: - name: tenant_id in: query required: false schema: anyOf: - type: string format: uuid - type: 'null' title: Tenant Id - name: x-aisoc-service-token in: header required: false schema: anyOf: - type: string - type: 'null' title: X-Aisoc-Service-Token responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ContextStatementsResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/autonomy-policy: get: tags: - autonomy-policy summary: Get Autonomy Policy description: 'Return the effective autonomy policy for the calling tenant. For each known action we surface the merged thresholds (defaults + DB overrides), the hard-coded defaults, and an ``overridden`` flag the UI uses to render a "modified from default" badge. Every action also carries whether it would *actually* execute without a human, resolved from the tenant''s maturity tier and the verb''s capability contract by ``app/services/autonomy_effective.py``. The thresholds and that answer are different facts, and the console previously derived the second from the first: a threshold below 1.0 was read as "auto-executes", which on stock defaults made nineteen of nineteen actions autonomous and put an Autopilot badge on a deployment that queues all of them.' operationId: get_autonomy_policy_api_v1_autonomy_policy_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AutonomyPolicyResponse' security: - HTTPBearer: [] /api/v1/autonomy-policy/{action}: put: tags: - autonomy-policy summary: Upsert Action Threshold description: 'Set (or update) the three-tier thresholds for one action. The new policy takes effect after the agent''s tenant-cache TTL expires or the cache is reset (``services/agents/app/policy/__init__.py`` ``reset_tenant_cache``).' operationId: upsert_action_threshold_api_v1_autonomy_policy__action__put security: - HTTPBearer: [] parameters: - name: action in: path required: true schema: type: string title: Action requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ThresholdUpdateRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ThresholdUpdateResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - autonomy-policy summary: Reset Action Threshold description: Reset a single action back to the hard-coded / YAML default. operationId: reset_action_threshold_api_v1_autonomy_policy__action__delete security: - HTTPBearer: [] parameters: - name: action in: path required: true schema: type: string title: Action responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/autonomy-policy/shadow-mode: get: tags: - autonomy-policy summary: Get Shadow Mode description: Which alert classes this tenant is measuring rather than acting on. operationId: get_shadow_mode_api_v1_autonomy_policy_shadow_mode_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ShadowModeResponse' security: - HTTPBearer: [] /api/v1/autonomy-policy/shadow-mode/{alert_class}: put: tags: - autonomy-policy summary: Set Shadow Mode description: 'Start or stop measuring one alert class. ``enabled_at`` is stamped on the transition into shadow and cleared on the way out, rather than being set on every write. A promotion window that reaches further back than the day measurement started is reaching back before there was anything to measure, and the only way a later reader can tell is if this column records the real start.' operationId: set_shadow_mode_api_v1_autonomy_policy_shadow_mode__alert_class__put security: - HTTPBearer: [] parameters: - name: alert_class in: path required: true schema: type: string title: Alert Class requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ShadowModeUpdateRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ShadowModeEntry' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/autonomy-policy/agreement: get: tags: - autonomy-policy summary: Get Agreement description: 'Rolling agreement between the agent and this tenant''s own analysts. The tenant comes from the credential. ``scope_kind`` and ``scope_key`` narrow *within* that tenant and cannot widen beyond it: the scope names a dimension from a fixed vocabulary, never a column, and the tenant predicate is applied whatever the scope says. Closures made in this console are reconciled on the way in rather than by a background job. The sweep is bounded and indexed, and doing it here means the number an operator is looking at includes the alert they closed a minute ago, which is exactly when they come to look.' operationId: get_agreement_api_v1_autonomy_policy_agreement_get security: - HTTPBearer: [] parameters: - name: scope_kind in: query required: false schema: type: string description: tenant, alert_class, rule, source or model default: tenant title: Scope Kind description: tenant, alert_class, rule, source or model - name: scope_key in: query required: false schema: type: string maxLength: 200 default: '*' title: Scope Key responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AgreementResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/autonomy-policy/grants: get: tags: - autonomy-policy summary: List Autonomy Grants description: 'Every capability this tenant holds or has held, re-checked on the way out. Reconciliation runs first. A grant whose evidence has slipped below the demotion floors is demoted here rather than being listed as current and quietly failing at dispatch, which would leave an operator reading a page that disagrees with the product.' operationId: list_autonomy_grants_api_v1_autonomy_policy_grants_get security: - HTTPBearer: [] responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/GrantListResponse' post: tags: - autonomy-policy summary: Request Autonomy Grant description: 'Ask for a capability. The evidence decides; an operator may overrule. A refusal is a 200 carrying `granted: false` and the reasons, not an error. Being told no by a safety control is the control working, and rendering it as a failure invites a client to retry it. The tenant comes from the credential. `scope_key` narrows within that tenant and cannot reach past it.' operationId: request_autonomy_grant_api_v1_autonomy_policy_grants_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PromotionRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PromotionResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - autonomy-policy summary: Revoke Autonomy Grant description: 'Hand a capability back. Audited as a revocation rather than as a demotion. A human deciding to stop and the evidence deciding for them are different events, and one word for both would make "was this taken away because the numbers slipped" unanswerable from the log.' operationId: revoke_autonomy_grant_api_v1_autonomy_policy_grants_delete security: - HTTPBearer: [] parameters: - name: scope_kind in: query required: true schema: type: string title: Scope Kind - name: scope_key in: query required: true schema: type: string maxLength: 200 title: Scope Key - name: capability in: query required: true schema: type: string title: Capability responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/business-context/rules: get: tags: - business-context summary: Get Rules description: 'Return the tenant''s current rule set. Always returns a 200 — a tenant with no rules gets an empty list and an empty YAML body. The editor uses the ``enabled`` flag to show the "rules paused" banner so analysts aren''t surprised when a saved rule isn''t taking effect.' operationId: get_rules_api_v1_business_context_rules_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RulesEnvelope' security: - HTTPBearer: [] post: tags: - business-context summary: Replace Rules description: 'Replace the tenant''s whole rule set with a new YAML document. The engine snapshot is swapped atomically *before* the write "completes" from the caller''s perspective, so the next evaluation on the same process sees the fresh rules within microseconds — well inside the 1s "save → preview" budget the task spec calls out. Multi-process deployments still honour the budget because the JSONB-backed reload (T3.5-followup) will SET version on save and every worker re-pulls on TTL miss.' operationId: replace_rules_api_v1_business_context_rules_post requestBody: content: application/json: schema: $ref: '#/components/schemas/ReplaceRulesRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RulesEnvelope' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/business-context/rules/{rule_id}: put: tags: - business-context summary: Update Rule description: 'Patch a single rule by id. The body is the YAML for *that one rule*; we splice it into the tenant''s rule list, replacing the existing entry of the same id (or appending if it''s a new id). Returns the same envelope shape as ``POST /rules`` so the editor can re-render in one round trip.' operationId: update_rule_api_v1_business_context_rules__rule_id__put security: - HTTPBearer: [] parameters: - name: rule_id in: path required: true schema: type: string title: Rule Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/app__api__v1__endpoints__business_context__UpdateRuleRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RulesEnvelope' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - business-context summary: Delete Rule operationId: delete_rule_api_v1_business_context_rules__rule_id__delete security: - HTTPBearer: [] parameters: - name: rule_id in: path required: true schema: type: string title: Rule Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/business-context/rules/preview: post: tags: - business-context summary: Preview Rules description: 'Dry-run the candidate YAML against a sample of alerts. Doesn''t persist anything; doesn''t modify the engine snapshot for the tenant. The response is shaped so the settings page can render a before/after table with a one-line "X of Y alerts changed" header.' operationId: preview_rules_api_v1_business_context_rules_preview_post requestBody: content: application/json: schema: $ref: '#/components/schemas/PreviewRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PreviewResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/nl-detection/translate: post: tags: - nl_detection summary: Translate Detection description: Convert a plain-English threat description into multi-platform detection rules. operationId: translate_detection_api_v1_nl_detection_translate_post requestBody: content: application/json: schema: $ref: '#/components/schemas/NLDetectionRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/NLDetectionResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/nl-detection/propose: post: tags: - nl_detection summary: Propose From Nl description: 'AI Detection Builder (W3.2): NL threat description -> Sigma rule + AUTO-GENERATED positive/negative fixtures -> non-circular eval-gate -> governed DRAFT proposal. Fixtures are DERIVED from the generated rule''s own selection (not invented by the LLM), so the gate genuinely proves the rule fires on a positive and stays quiet on a negative. If the rule can''t be auto-verified, no proposal is opened and the human is asked to supply fixtures — the honest outcome.' operationId: propose_from_nl_api_v1_nl_detection_propose_post requestBody: content: application/json: schema: $ref: '#/components/schemas/NLProposeRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/NLProposeResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/nl-query/translate: post: tags: - nl_query summary: Translate a natural-language security question to ES|QL / SPL / KQL operationId: translate_query_api_v1_nl_query_translate_post requestBody: content: application/json: schema: $ref: '#/components/schemas/NLQueryTranslateRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/NLQueryTranslateResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/nl-query/execute: post: tags: - nl_query summary: Translate NL question and execute ES|QL against Elasticsearch operationId: execute_query_api_v1_nl_query_execute_post requestBody: content: application/json: schema: $ref: '#/components/schemas/NLQueryExecuteRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/NLQueryExecuteResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/identity-timeline/build: post: tags: - identity_timeline summary: Build identity-centric investigation timeline operationId: build_timeline_api_v1_identity_timeline_build_post requestBody: content: application/json: schema: $ref: '#/components/schemas/BuildTimelineRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/IdentityTimeline' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/identity-timeline: get: tags: - identity_timeline summary: Quick timeline lookup for an identity operationId: get_timeline_api_v1_identity_timeline_get security: - HTTPBearer: [] parameters: - name: identity_kind in: query required: true schema: enum: - user - device - service_account - ip type: string title: Identity Kind - name: identity_value in: query required: true schema: type: string minLength: 1 title: Identity Value - name: hours in: query required: false schema: type: integer maximum: 8760 minimum: 1 default: 72 title: Hours responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/IdentityTimeline' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/translation/translate: post: tags: - translation summary: Translate a detection rule across formats operationId: translate_rule_api_v1_translation_translate_post requestBody: content: application/json: schema: $ref: '#/components/schemas/TranslateRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TranslateResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/translation/formats: get: tags: - translation summary: List supported detection rule formats operationId: list_formats_api_v1_translation_formats_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/FormatsResponse' /api/v1/hunts: get: tags: - hunts summary: List hunt hypotheses operationId: list_hunts_api_v1_hunts_get security: - HTTPBearer: [] parameters: - name: status in: query required: false schema: anyOf: - type: string - type: 'null' title: Status - name: priority in: query required: false schema: anyOf: - type: string - type: 'null' title: Priority - 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: array items: $ref: '#/components/schemas/app__api__v1__endpoints__hunts__HuntResponse' title: Response List Hunts Api V1 Hunts Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - hunts summary: Create hunt hypothesis operationId: create_hunt_api_v1_hunts_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateHuntRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/app__api__v1__endpoints__hunts__HuntResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/hunts/{hunt_id}: get: tags: - hunts summary: Get hunt operationId: get_hunt_api_v1_hunts__hunt_id__get security: - HTTPBearer: [] parameters: - name: hunt_id in: path required: true schema: type: string format: uuid title: Hunt Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/app__api__v1__endpoints__hunts__HuntResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' patch: tags: - hunts summary: Update hunt operationId: update_hunt_api_v1_hunts__hunt_id__patch security: - HTTPBearer: [] parameters: - name: hunt_id in: path required: true schema: type: string format: uuid title: Hunt Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateHuntRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/app__api__v1__endpoints__hunts__HuntResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/hunts/{hunt_id}/run: post: tags: - hunts summary: Execute hunt query operationId: run_hunt_api_v1_hunts__hunt_id__run_post security: - HTTPBearer: [] parameters: - name: hunt_id in: path required: true schema: type: string format: uuid title: Hunt Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RunHuntRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/HuntRunResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/hunts/{hunt_id}/runs: get: tags: - hunts summary: List hunt runs operationId: list_runs_api_v1_hunts__hunt_id__runs_get security: - HTTPBearer: [] parameters: - name: hunt_id in: path required: true schema: type: string format: uuid title: Hunt Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/HuntRunResponse' title: Response List Runs Api V1 Hunts Hunt Id Runs Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/hunts/{hunt_id}/findings: post: tags: - hunts summary: Append findings operationId: add_findings_api_v1_hunts__hunt_id__findings_post security: - HTTPBearer: [] parameters: - name: hunt_id in: path required: true schema: type: string format: uuid title: Hunt Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AddFindingsRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/app__api__v1__endpoints__hunts__HuntResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/saved-hunts: get: tags: - saved-hunts summary: List Saved Hunts description: 'Return every saved hunt visible in the caller''s tenant. Sorted by ``updated_at DESC`` so the most recently re-run hunt lands first; the UI renders this list as the "Saved hunts" sidebar.' operationId: list_saved_hunts_api_v1_saved_hunts_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/SavedHuntModel' type: array title: Response List Saved Hunts Api V1 Saved Hunts Get security: - HTTPBearer: [] post: tags: - saved-hunts summary: Create Saved Hunt description: Translate the NL question and persist the hunt. operationId: create_saved_hunt_api_v1_saved_hunts_post requestBody: content: application/json: schema: $ref: '#/components/schemas/CreateSavedHuntRequest' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SavedHuntModel' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/saved-hunts/{hunt_id}: get: tags: - saved-hunts summary: Get Saved Hunt description: Fetch one saved hunt. operationId: get_saved_hunt_api_v1_saved_hunts__hunt_id__get security: - HTTPBearer: [] parameters: - name: hunt_id in: path required: true schema: type: string title: Hunt Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SavedHuntModel' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - saved-hunts summary: Delete Saved Hunt description: Delete a saved hunt owned by the caller's tenant. operationId: delete_saved_hunt_api_v1_saved_hunts__hunt_id__delete security: - HTTPBearer: [] parameters: - name: hunt_id in: path required: true schema: type: string title: Hunt Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/saved-hunts/{hunt_id}/run: post: tags: - saved-hunts summary: Run Saved Hunt description: 'Re-translate the saved NL question and stamp ``last_run_at``. Why re-translate on every run? The translator is the platform''s source of truth for "what does this question mean today?" — improving the translator (e.g. adding a new field alias) should automatically benefit saved hunts the next time they''re executed without an analyst having to re-save them. The stored ``translated_query`` is the snapshot taken at save time and serves as a baseline for diffing.' operationId: run_saved_hunt_api_v1_saved_hunts__hunt_id__run_post security: - HTTPBearer: [] parameters: - name: hunt_id in: path required: true schema: type: string title: Hunt Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RunSavedHuntResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/phishing/submit: post: tags: - phishing summary: Submit artifact for phishing triage operationId: submit_api_v1_phishing_submit_post requestBody: content: application/json: schema: $ref: '#/components/schemas/SubmitRequest' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SubmissionResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/phishing/reports: post: tags: - phishing summary: Intake a reported phishing message description: 'Take a reported message from one of the three intake doors. Distinct from ``/submit``, which takes an artefact an analyst typed. This takes a *message somebody received*, so it carries the two fields the response turns on and ``/submit`` has never had: the internet message id that finds the copies, and the recipients that bound the purge.' operationId: report_api_v1_phishing_reports_post requestBody: content: application/json: schema: $ref: '#/components/schemas/ReportRequest' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ReportResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/phishing/campaigns: get: tags: - phishing summary: List phishing campaigns operationId: list_campaigns_api_v1_phishing_campaigns_get security: - HTTPBearer: [] 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: type: array items: type: object additionalProperties: true title: Response List Campaigns Api V1 Phishing Campaigns Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/phishing/campaigns/{campaign_id}: get: tags: - phishing summary: Get a phishing campaign and its recipients operationId: get_campaign_api_v1_phishing_campaigns__campaign_id__get security: - HTTPBearer: [] parameters: - name: campaign_id in: path required: true schema: type: string format: uuid title: Campaign Id responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Get Campaign Api V1 Phishing Campaigns Campaign Id Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/phishing/campaigns/{campaign_id}/purge: post: tags: - phishing summary: Propose or run a purge across a campaign's recipients description: 'One proposal, one dispatch per mailbox, each graded on its own. ``actions:execute`` rather than ``cases:write``: this reaches out of the platform and removes mail, so it is gated like every other action surface in this service rather than like a case note. Deliberately **not** ``actions:contain``, which is what the capability contract declares. That string is held by no role in ``ROLE_PERMISSIONS`` except the two wildcards, so gating the route on it would ship a route only an administrator could reach while reading as though analysts could. The contract''s permission is enforced at the dispatcher, where a principal exists; here the question is who may *ask*, and ``actions:execute`` is the permission this service already means by that — its own comment says it "gates the dry-run preview only; a live containment goes through the approval path", which is exactly the split between ``execute: false`` and ``execute: true`` below.' operationId: purge_campaign_api_v1_phishing_campaigns__campaign_id__purge_post security: - HTTPBearer: [] parameters: - name: campaign_id in: path required: true schema: type: string format: uuid title: Campaign Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PurgeRequest' responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Purge Campaign Api V1 Phishing Campaigns Campaign Id Purge Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/phishing/campaigns/{campaign_id}/restore: post: tags: - phishing summary: Restore a purged campaign's messages description: 'The reverse of the purge, over the mailboxes it actually executed on. Only rows the purge executed on are restored. Attempting a restore on a mailbox that was previewed, failed, or never held the message would report work that did not happen.' operationId: restore_campaign_api_v1_phishing_campaigns__campaign_id__restore_post security: - HTTPBearer: [] parameters: - name: campaign_id in: path required: true schema: type: string format: uuid title: Campaign Id responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Restore Campaign Api V1 Phishing Campaigns Campaign Id Restore Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/phishing/submissions: get: tags: - phishing summary: List phishing submissions operationId: list_submissions_api_v1_phishing_submissions_get security: - HTTPBearer: [] parameters: - name: verdict in: query required: false schema: anyOf: - type: string - type: 'null' title: Verdict - 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: array items: $ref: '#/components/schemas/SubmissionResponse' title: Response List Submissions Api V1 Phishing Submissions Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/phishing/{submission_id}: get: tags: - phishing summary: Get submission operationId: get_submission_api_v1_phishing__submission_id__get security: - HTTPBearer: [] parameters: - name: submission_id in: path required: true schema: type: string format: uuid title: Submission Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SubmissionResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/phishing/{submission_id}/retriage: post: tags: - phishing summary: Re-run triage on submission operationId: retriage_api_v1_phishing__submission_id__retriage_post security: - HTTPBearer: [] parameters: - name: submission_id in: path required: true schema: type: string format: uuid title: Submission Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SubmissionResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/sandbox/providers: get: tags: - sandbox summary: Analysis providers and this tenant's upload consent operationId: providers_api_v1_sandbox_providers_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ProvidersResponse' security: - HTTPBearer: [] /api/v1/sandbox/lookup: post: tags: - sandbox summary: Look a file hash up (nothing is uploaded) operationId: lookup_api_v1_sandbox_lookup_post requestBody: content: application/json: schema: $ref: '#/components/schemas/LookupRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SandboxResultResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/sandbox/files: post: tags: - sandbox summary: Analyse a file, uploading only if policy allows operationId: submit_file_api_v1_sandbox_files_post requestBody: content: application/json: schema: $ref: '#/components/schemas/SubmitFileRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SandboxResultResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/sandbox/urls: post: tags: - sandbox summary: Submit a URL for analysis operationId: submit_url_api_v1_sandbox_urls_post requestBody: content: application/json: schema: $ref: '#/components/schemas/SubmitUrlRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SandboxResultResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/sandbox/analyses/{provider_name}/{handle}: get: tags: - sandbox summary: Poll an analysis operationId: get_analysis_api_v1_sandbox_analyses__provider_name___handle__get security: - HTTPBearer: [] parameters: - name: provider_name in: path required: true schema: type: string title: Provider Name - name: handle in: path required: true schema: type: string title: Handle responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SandboxResultResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/sandbox/consent: put: tags: - sandbox summary: Allow or refuse file uploads to a provider operationId: put_consent_api_v1_sandbox_consent_put requestBody: content: application/json: schema: $ref: '#/components/schemas/ConsentRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ConsentResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/evaluations/replay/capabilities: get: tags: - evaluations summary: Which connectors can be replayed on this deployment operationId: capabilities_api_v1_evaluations_replay_capabilities_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CapabilitiesResponse' security: - HTTPBearer: [] /api/v1/evaluations/replay: post: tags: - evaluations summary: Start a replay evaluation against this tenant's own history description: 'Queue a run and return its id immediately. 202 rather than 200: replaying a few hundred findings through a model is not a request-response operation, and a route that blocked on it would time out at whichever proxy is in front of it while the work continued.' operationId: start_replay_api_v1_evaluations_replay_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/StartReplayRequest' responses: '202': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/EvaluationSummary' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' get: tags: - evaluations summary: This tenant's replay evaluations operationId: list_replays_api_v1_evaluations_replay_get security: - HTTPBearer: [] parameters: - 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: array items: $ref: '#/components/schemas/EvaluationSummary' title: Response List Replays Api V1 Evaluations Replay Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/evaluations/replay/{evaluation_id}: get: tags: - evaluations summary: One replay evaluation operationId: get_replay_api_v1_evaluations_replay__evaluation_id__get security: - HTTPBearer: [] parameters: - name: evaluation_id in: path required: true schema: type: string format: uuid title: Evaluation Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/EvaluationDetail' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/evaluations/replay/{evaluation_id}/decisions: get: tags: - evaluations summary: The decisions behind a report description: 'Every replayed finding, so a disputed number can be re-derived. Read separately from the report because these rows carry the evidence handed to triage and are measured in megabytes, and the page that shows the headline should not pay for them.' operationId: get_replay_decisions_api_v1_evaluations_replay__evaluation_id__decisions_get security: - HTTPBearer: [] parameters: - name: evaluation_id in: path required: true schema: type: string format: uuid title: Evaluation Id - name: limit in: query required: false schema: type: integer maximum: 2000 minimum: 1 default: 2000 title: Limit responses: '200': description: Successful Response content: application/json: schema: type: array items: type: object additionalProperties: true title: Response Get Replay Decisions Api V1 Evaluations Replay Evaluation Id Decisions Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/evaluations/replay/{evaluation_id}/export: get: tags: - evaluations summary: Export a report as JSON, Markdown or PDF description: 'Serve the stored report, not a fresh render of it. All three formats come from the artefact written when the run completed. Re-rendering later would produce a document that no longer matches what the operator read, and this phase''s acceptance bar is a report that reproduces byte for byte.' operationId: export_replay_api_v1_evaluations_replay__evaluation_id__export_get security: - HTTPBearer: [] parameters: - name: evaluation_id in: path required: true schema: type: string format: uuid title: Evaluation Id - name: format in: query required: false schema: enum: - json - markdown - pdf type: string default: markdown title: Format - name: exclude_latency in: query required: false schema: type: boolean description: Replace the two wall-clock latency figures with a note. Everything else in the report is a property of the input and the code, so two runs over one pinned window are then byte-identical; latency measures the host and never will be. default: false title: Exclude Latency description: Replace the two wall-clock latency figures with a note. Everything else in the report is a property of the input and the code, so two runs over one pinned window are then byte-identical; latency measures the host and never will be. responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/kb/ingest: post: tags: - knowledge_base summary: Ingest document into knowledge base operationId: ingest_api_v1_kb_ingest_post requestBody: content: application/json: schema: $ref: '#/components/schemas/IngestRequest' required: true responses: '201': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/KBDocResponse' type: array title: Response Ingest Api V1 Kb Ingest Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/kb/documents: get: tags: - knowledge_base summary: List KB documents operationId: list_documents_api_v1_kb_documents_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/KBDocResponse' type: array title: Response List Documents Api V1 Kb Documents Get security: - HTTPBearer: [] /api/v1/kb/documents/{doc_id}: get: tags: - knowledge_base summary: Get KB document operationId: get_document_api_v1_kb_documents__doc_id__get security: - HTTPBearer: [] parameters: - name: doc_id in: path required: true schema: type: string format: uuid title: Doc Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/KBDocResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - knowledge_base summary: Remove KB document operationId: delete_document_api_v1_kb_documents__doc_id__delete security: - HTTPBearer: [] parameters: - name: doc_id in: path required: true schema: type: string format: uuid title: Doc Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/kb/query: post: tags: - knowledge_base summary: Search knowledge base + optional LLM synthesis operationId: query_kb_api_v1_kb_query_post requestBody: content: application/json: schema: $ref: '#/components/schemas/QueryRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/QueryResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/taxii2/: get: tags: - taxii summary: Discovery description: '``GET /taxii2/`` — what this server is and where its API roots are.' operationId: discovery_api_v1_taxii2__get responses: '200': description: Successful Response content: application/json: schema: {} security: - HTTPBearer: [] /api/v1/taxii2/{api_root}/: get: tags: - taxii summary: Api Root Information description: '``GET /taxii2/{api-root}/`` — the limits a client must respect.' operationId: api_root_information_api_v1_taxii2__api_root___get security: - HTTPBearer: [] parameters: - name: api_root in: path required: true schema: type: string title: Api Root responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/taxii2/{api_root}/collections/: get: tags: - taxii summary: List Collections description: '``GET /taxii2/{api-root}/collections/``.' operationId: list_collections_api_v1_taxii2__api_root__collections__get security: - HTTPBearer: [] parameters: - name: api_root in: path required: true schema: type: string title: Api Root responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/taxii2/{api_root}/collections/{collection_ref}/: get: tags: - taxii summary: Get Collection description: '``GET /taxii2/{api-root}/collections/{id}/``.' operationId: get_collection_api_v1_taxii2__api_root__collections__collection_ref___get security: - HTTPBearer: [] parameters: - name: api_root in: path required: true schema: type: string title: Api Root - name: collection_ref in: path required: true schema: type: string title: Collection Ref responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/taxii2/{api_root}/collections/{collection_ref}/objects/: get: tags: - taxii summary: Get Objects description: '``GET /taxii2/{api-root}/collections/{id}/objects/``.' operationId: get_objects_api_v1_taxii2__api_root__collections__collection_ref__objects__get security: - HTTPBearer: [] parameters: - name: api_root in: path required: true schema: type: string title: Api Root - name: collection_ref in: path required: true schema: type: string title: Collection Ref - name: added_after in: query required: false schema: anyOf: - type: string - type: 'null' title: Added After - name: limit in: query required: false schema: anyOf: - type: integer maximum: 1000 minimum: 1 - type: 'null' title: Limit - name: next in: query required: false schema: anyOf: - type: string - type: 'null' title: Next - name: match[id] in: query required: false schema: anyOf: - type: string - type: 'null' title: Match[Id] - name: match[type] in: query required: false schema: anyOf: - type: string - type: 'null' title: Match[Type] responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - taxii summary: Refuse Write description: 'Inbound TAXII writes are refused, and say why. A 405 would read as "wrong verb" and send the integrator looking for the right one. This is a deliberate posture, so it is a 403 that names it.' operationId: refuse_write_api_v1_taxii2__api_root__collections__collection_ref__objects__post security: - HTTPBearer: [] parameters: - name: api_root in: path required: true schema: type: string title: Api Root - name: collection_ref in: path required: true schema: type: string title: Collection Ref responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/taxii2/{api_root}/collections/{collection_ref}/manifest/: get: tags: - taxii summary: Get Manifest description: '``GET /taxii2/{api-root}/collections/{id}/manifest/``. The manifest is what a client polls to decide what to fetch, so it pages by the same cursor as ``objects``. Two different orderings would let a client hold a manifest entry it can never reach.' operationId: get_manifest_api_v1_taxii2__api_root__collections__collection_ref__manifest__get security: - HTTPBearer: [] parameters: - name: api_root in: path required: true schema: type: string title: Api Root - name: collection_ref in: path required: true schema: type: string title: Collection Ref - name: added_after in: query required: false schema: anyOf: - type: string - type: 'null' title: Added After - name: limit in: query required: false schema: anyOf: - type: integer maximum: 1000 minimum: 1 - type: 'null' title: Limit - name: next in: query required: false schema: anyOf: - type: string - type: 'null' title: Next responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/taxii/collections: get: tags: - taxii summary: List Managed Collections operationId: list_managed_collections_api_v1_taxii_collections_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/CollectionOut' type: array title: Response List Managed Collections Api V1 Taxii Collections Get security: - HTTPBearer: [] post: tags: - taxii summary: Create Managed Collection operationId: create_managed_collection_api_v1_taxii_collections_post requestBody: content: application/json: schema: $ref: '#/components/schemas/CollectionCreate' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CollectionOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/taxii/collections/{collection_id}: delete: tags: - taxii summary: Delete Managed Collection operationId: delete_managed_collection_api_v1_taxii_collections__collection_id__delete security: - HTTPBearer: [] parameters: - name: collection_id in: path required: true schema: type: string format: uuid title: Collection Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/taxii/unpublished-types: get: tags: - taxii summary: Unpublished Types description: 'Which IOC types this server will not publish, and why. An absence a tenant cannot explain reads as a bug. Published as data rather than documented in prose so the answer cannot drift from the table the projection actually uses.' operationId: unpublished_types_api_v1_taxii_unpublished_types_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Unpublished Types Api V1 Taxii Unpublished Types Get security: - HTTPBearer: [] /api/v1/threat-intel/indicators: get: tags: - threat-intel summary: List Feed Indicators description: 'Indicators the feed scheduler has collected, from `services/threatintel`. The console has called this path since the IOC inbox shipped and nothing served it — the page 404''d, and for a while rendered five invented IOCs instead. `services/threatintel` and its Qdrant store are in CORE now, and the CISA Known Exploited Vulnerabilities catalog is public and keyless, so a default install has real indicators to show within a poll interval. Tenant scope is enforced upstream: this proxy asserts the caller''s tenant in the same header the connectors proxy uses, and the threatintel route filters on it plus the `shared` sentinel that public feed intel is written under.' operationId: list_feed_indicators_api_v1_threat_intel_indicators_get security: - HTTPBearer: [] parameters: - name: type in: query required: false schema: anyOf: - type: string - type: 'null' description: filter to one indicator type title: Type description: filter to one indicator type - name: tag in: query required: false schema: anyOf: - type: string - type: 'null' title: Tag - name: q in: query required: false schema: anyOf: - type: string - type: 'null' description: substring match on value or description title: Q description: substring match on value or description responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/FeedIndicatorsResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/threat-intel/iocs: get: tags: - threat-intel summary: List Iocs description: '**This tenant''s own** IOCs, from Postgres `threat_intel_iocs`. Not the platform''s feed intelligence, and the distinction is the whole content of issue #1278: this route and `/indicators` sit next to each other and read different stores. `/indicators` serves the CISA KEV catalogue out of Qdrant — about 1,700 entries on a stock CORE install. This table is written **only** by `POST /threat-intel/iocs`, so it is empty until somebody submits to it, and an empty response here is not evidence that the KEV feed failed.' operationId: list_iocs_api_v1_threat_intel_iocs_get security: - HTTPBearer: [] parameters: - name: ioc_type in: query required: false schema: anyOf: - type: string - type: 'null' title: Ioc Type - name: severity in: query required: false schema: anyOf: - type: string - type: 'null' title: Severity - name: is_active in: query required: false schema: anyOf: - type: boolean - type: 'null' title: Is Active - name: limit in: query required: false schema: type: integer maximum: 500 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: array items: $ref: '#/components/schemas/IOCOut' title: Response List Iocs Api V1 Threat Intel Iocs Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - threat-intel summary: Create Ioc operationId: create_ioc_api_v1_threat_intel_iocs_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/IOCCreate' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/IOCOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/threat-intel/iocs/{ioc_id}: get: tags: - threat-intel summary: Get Ioc operationId: get_ioc_api_v1_threat_intel_iocs__ioc_id__get security: - HTTPBearer: [] parameters: - name: ioc_id in: path required: true schema: type: string format: uuid title: Ioc Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/IOCOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - threat-intel summary: Delete Ioc operationId: delete_ioc_api_v1_threat_intel_iocs__ioc_id__delete security: - HTTPBearer: [] parameters: - name: ioc_id in: path required: true schema: type: string format: uuid title: Ioc Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/threat-intel/actors: get: tags: - threat-intel summary: List Actors operationId: list_actors_api_v1_threat_intel_actors_get security: - HTTPBearer: [] parameters: - name: is_active in: query required: false schema: anyOf: - type: boolean - type: 'null' title: Is Active - name: limit in: query required: false schema: type: integer maximum: 200 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: array items: $ref: '#/components/schemas/ThreatActorOut' title: Response List Actors Api V1 Threat Intel Actors Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - threat-intel summary: Create Actor operationId: create_actor_api_v1_threat_intel_actors_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ThreatActorCreate' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ThreatActorOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/threat-intel/feeds: get: tags: - threat-intel summary: List Feeds description: '**Feeds this tenant has registered**, from Postgres `threat_intel_feeds`. Written only by `POST /threat-intel/feeds`, so it is empty until an operator adds one. It deliberately does **not** list the platform''s built-in feeds: the CISA KEV poller is an in-process job in `services/threatintel` and owns no row here, which is why a stock CORE install sees `[]` here while `/indicators` serves the catalogue that poller fetched (issue #1278). Listing the built-in poller here would mean synthesising an id and a `tenant_id` for a row that does not exist, which is the fabrication pattern this project has paid for before; `/indicators` is where its output is observable.' operationId: list_feeds_api_v1_threat_intel_feeds_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/FeedOut' type: array title: Response List Feeds Api V1 Threat Intel Feeds Get security: - HTTPBearer: [] post: tags: - threat-intel summary: Create Feed operationId: create_feed_api_v1_threat_intel_feeds_post requestBody: content: application/json: schema: $ref: '#/components/schemas/FeedCreate' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/FeedOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/threat-intel/feeds/{feed_id}: delete: tags: - threat-intel summary: Delete Feed operationId: delete_feed_api_v1_threat_intel_feeds__feed_id__delete security: - HTTPBearer: [] parameters: - name: feed_id in: path required: true schema: type: string format: uuid title: Feed Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/posture/findings: get: tags: - posture summary: List Findings operationId: list_findings_api_v1_posture_findings_get security: - HTTPBearer: [] parameters: - name: cloud_provider in: query required: false schema: anyOf: - type: string - type: 'null' title: Cloud Provider - name: severity in: query required: false schema: anyOf: - type: string - type: 'null' title: Severity - name: status in: query required: false schema: anyOf: - type: string - type: 'null' title: Status - name: rule_id in: query required: false schema: anyOf: - type: string - type: 'null' title: Rule Id - name: limit in: query required: false schema: type: integer maximum: 500 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: array items: $ref: '#/components/schemas/FindingOut' title: Response List Findings Api V1 Posture Findings Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - posture summary: Ingest Finding operationId: ingest_finding_api_v1_posture_findings_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/FindingCreate' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/FindingOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/posture/findings/{finding_id}: get: tags: - posture summary: Get Finding operationId: get_finding_api_v1_posture_findings__finding_id__get security: - HTTPBearer: [] parameters: - name: finding_id in: path required: true schema: type: string format: uuid title: Finding Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/FindingOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/posture/findings/{finding_id}/suppress: post: tags: - posture summary: Suppress Finding operationId: suppress_finding_api_v1_posture_findings__finding_id__suppress_post security: - HTTPBearer: [] parameters: - name: finding_id in: path required: true schema: type: string format: uuid title: Finding Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SuppressRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/FindingOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/posture/findings/{finding_id}/resolve: post: tags: - posture summary: Resolve Finding operationId: resolve_finding_api_v1_posture_findings__finding_id__resolve_post security: - HTTPBearer: [] parameters: - name: finding_id in: path required: true schema: type: string format: uuid title: Finding Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/FindingOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/posture/summary: get: tags: - posture summary: Get Summary operationId: get_summary_api_v1_posture_summary_get security: - HTTPBearer: [] parameters: - name: cloud_provider in: query required: false schema: anyOf: - type: string - type: 'null' title: Cloud Provider responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/PostureSummary' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/posture/scans: get: tags: - posture summary: List Scans operationId: list_scans_api_v1_posture_scans_get security: - HTTPBearer: [] parameters: - name: limit in: query required: false schema: type: integer maximum: 100 default: 20 title: Limit - name: offset in: query required: false schema: type: integer minimum: 0 default: 0 title: Offset responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/ScanRunOut' title: Response List Scans Api V1 Posture Scans Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/posture/scan: post: tags: - posture summary: Cspm Scan description: 'Agentless CSPM MVP (W6.2): evaluate a cloud-resource snapshot against the misconfiguration checks, returning findings + a severity summary + the auto-generated compliance evidence (W6.1). Stateless — the findings store endpoints above persist what an operator chooses to keep.' operationId: cspm_scan_api_v1_posture_scan_post requestBody: content: application/json: schema: $ref: '#/components/schemas/CspmScanRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CspmScanResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/posture/destinations/preview: post: tags: - posture summary: Destination Preview description: Preview the exact payload a destination would send (W6.3) — no send. operationId: destination_preview_api_v1_posture_destinations_preview_post requestBody: content: application/json: schema: $ref: '#/components/schemas/DestinationPreviewRequest' required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Destination Preview Api V1 Posture Destinations Preview Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/easm/scan: post: tags: - easm summary: Trigger Easm Scan description: 'Trigger an EASM discovery scan for a tenant (Tier 3.6). Runs passive connectors (Shodan / Censys) and, when enabled, an active TCP connect probe. Results are upserted into ``external_assets`` and drift events written to ``external_asset_drift``.' operationId: trigger_easm_scan_api_v1_easm_scan_post requestBody: content: application/json: schema: $ref: '#/components/schemas/ScanRequest' required: true responses: '202': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Trigger Easm Scan Api V1 Easm Scan Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/easm/assets: get: tags: - easm summary: List External Assets description: 'List externally discovered assets for a tenant. ``tenant_id`` defaults to the authenticated user''s tenant, so the analyst console can hit ``/easm/assets`` without explicit tenant scoping. A user can never request another tenant''s assets — supplying a foreign ``tenant_id`` returns 403.' operationId: list_external_assets_api_v1_easm_assets_get security: - HTTPBearer: [] parameters: - name: tenant_id in: query required: false schema: anyOf: - type: string format: uuid - type: 'null' title: Tenant Id - name: asset_type in: query required: false schema: anyOf: - $ref: '#/components/schemas/ExternalAssetType' - type: 'null' title: Asset Type - name: limit in: query required: false schema: type: integer maximum: 200 default: 50 title: Limit responses: '200': description: Successful Response content: application/json: schema: type: array items: type: object additionalProperties: true title: Response List External Assets Api V1 Easm Assets Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/easm/drift: get: tags: - easm summary: List External Asset Drift description: 'List drift events (new ports, certs, sub-domains, etc.). A user can never request another tenant''s drift events — supplying a foreign ``tenant_id`` returns 403.' operationId: list_external_asset_drift_api_v1_easm_drift_get security: - HTTPBearer: [] parameters: - name: tenant_id in: query required: false schema: anyOf: - type: string format: uuid - type: 'null' title: Tenant Id - name: external_asset_id in: query required: false schema: anyOf: - type: string format: uuid - type: 'null' title: External Asset Id - name: limit in: query required: false schema: type: integer maximum: 200 default: 50 title: Limit responses: '200': description: Successful Response content: application/json: schema: type: array items: type: object additionalProperties: true title: Response List External Asset Drift Api V1 Easm Drift Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/identity-graph/nodes: get: tags: - identity-graph summary: List Nodes operationId: list_nodes_api_v1_identity_graph_nodes_get security: - HTTPBearer: [] parameters: - name: node_type in: query required: false schema: anyOf: - type: string - type: 'null' title: Node Type - name: source_system in: query required: false schema: anyOf: - type: string - type: 'null' title: Source System - name: min_risk in: query required: false schema: anyOf: - type: number - type: 'null' title: Min Risk - name: limit in: query required: false schema: type: integer maximum: 500 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: array items: $ref: '#/components/schemas/NodeOut' title: Response List Nodes Api V1 Identity Graph Nodes Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - identity-graph summary: Create Node operationId: create_node_api_v1_identity_graph_nodes_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/NodeCreate' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/NodeOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/identity-graph/nodes/{node_id}: get: tags: - identity-graph summary: Get Node operationId: get_node_api_v1_identity_graph_nodes__node_id__get security: - HTTPBearer: [] parameters: - name: node_id in: path required: true schema: type: string format: uuid title: Node Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/NodeOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/identity-graph/nodes/{node_id}/edges: get: tags: - identity-graph summary: Get Node Edges operationId: get_node_edges_api_v1_identity_graph_nodes__node_id__edges_get security: - HTTPBearer: [] parameters: - name: node_id in: path required: true schema: type: string format: uuid title: Node Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/EdgeOut' title: Response Get Node Edges Api V1 Identity Graph Nodes Node Id Edges Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/identity-graph/edges: get: tags: - identity-graph summary: List Edges operationId: list_edges_api_v1_identity_graph_edges_get security: - HTTPBearer: [] parameters: - name: edge_type in: query required: false schema: anyOf: - type: string - type: 'null' title: Edge Type - name: limit in: query required: false schema: type: integer maximum: 1000 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/EdgeOut' title: Response List Edges Api V1 Identity Graph Edges Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - identity-graph summary: Create Edge operationId: create_edge_api_v1_identity_graph_edges_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/EdgeCreate' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/EdgeOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/identity-graph/alert-links: post: tags: - identity-graph summary: Link Alert To Identity operationId: link_alert_to_identity_api_v1_identity_graph_alert_links_post requestBody: content: application/json: schema: $ref: '#/components/schemas/AlertLinkCreate' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AlertLinkOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/identity-graph/alert-links/{alert_id}: get: tags: - identity-graph summary: Get Alert Identity Links operationId: get_alert_identity_links_api_v1_identity_graph_alert_links__alert_id__get security: - HTTPBearer: [] parameters: - name: alert_id in: path required: true schema: type: string format: uuid title: Alert Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/AlertLinkOut' title: Response Get Alert Identity Links Api V1 Identity Graph Alert Links Alert Id Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/identity-resolution/links: get: tags: - identity-resolution summary: Every proposed link touching one identity operationId: list_links_api_v1_identity_resolution_links_get security: - HTTPBearer: [] parameters: - name: key in: query required: true schema: type: string description: A source-scoped identity, e.g. okta:alice@example.com title: Key description: A source-scoped identity, e.g. okta:alice@example.com - name: include_weak in: query required: false schema: type: boolean default: true title: Include Weak responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response List Links Api V1 Identity Resolution Links Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/identity-resolution/cluster: get: tags: - identity-resolution summary: Who else is this person description: Merge-strength links only, bounded. See the store for why both. operationId: cluster_api_v1_identity_resolution_cluster_get security: - HTTPBearer: [] parameters: - name: key in: query required: true schema: type: string title: Key - name: max_depth in: query required: false schema: type: integer maximum: 6 minimum: 1 default: 3 title: Max Depth responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Cluster Api V1 Identity Resolution Cluster Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/identity-resolution/resolve: post: tags: - identity-resolution summary: Resolve a set of identities into links description: 'Run resolution over the supplied records. ``settings:write`` rather than a read permission because ``persist`` writes the tenant''s identity graph, and a caller who may not change tenant configuration should not be able to decide who is the same person.' operationId: resolve_api_v1_identity_resolution_resolve_post requestBody: content: application/json: schema: $ref: '#/components/schemas/ResolveRequest' required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Resolve Api V1 Identity Resolution Resolve Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/identity-resolution/links/{link_id}/decide: post: tags: - identity-resolution summary: Confirm or reject a proposed link description: A 404 for another tenant's link, so this is not an existence oracle. operationId: decide_api_v1_identity_resolution_links__link_id__decide_post security: - HTTPBearer: [] parameters: - name: link_id in: path required: true schema: type: string format: uuid title: Link Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DecideRequest' responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Decide Api V1 Identity Resolution Links Link Id Decide Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/reports/templates: get: tags: - reports summary: List Templates operationId: list_templates_api_v1_reports_templates_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/TemplateOut' type: array title: Response List Templates Api V1 Reports Templates Get security: - HTTPBearer: [] post: tags: - reports summary: Create Template operationId: create_template_api_v1_reports_templates_post requestBody: content: application/json: schema: $ref: '#/components/schemas/TemplateCreate' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TemplateOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/reports/templates/{template_id}: get: tags: - reports summary: Get Template operationId: get_template_api_v1_reports_templates__template_id__get security: - HTTPBearer: [] parameters: - name: template_id in: path required: true schema: type: string format: uuid title: Template Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TemplateOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - reports summary: Delete Template operationId: delete_template_api_v1_reports_templates__template_id__delete security: - HTTPBearer: [] parameters: - name: template_id in: path required: true schema: type: string format: uuid title: Template Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/reports/artefacts: get: tags: - reports summary: List Artefacts operationId: list_artefacts_api_v1_reports_artefacts_get security: - HTTPBearer: [] parameters: - name: report_type in: query required: false schema: anyOf: - type: string - type: 'null' title: Report Type - name: status in: query required: false schema: anyOf: - type: string - type: 'null' title: Status - name: limit in: query required: false schema: type: integer maximum: 100 default: 20 title: Limit - name: offset in: query required: false schema: type: integer minimum: 0 default: 0 title: Offset responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/ArtefactOut' title: Response List Artefacts Api V1 Reports Artefacts Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/reports/generate: post: tags: - reports summary: Generate Report description: Enqueue a report generation job. Returns the artefact record immediately with status='pending'. operationId: generate_report_api_v1_reports_generate_post requestBody: content: application/json: schema: $ref: '#/components/schemas/GenerateRequest' required: true responses: '202': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ArtefactOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/reports/artefacts/{artefact_id}: get: tags: - reports summary: Get Artefact operationId: get_artefact_api_v1_reports_artefacts__artefact_id__get security: - HTTPBearer: [] parameters: - name: artefact_id in: path required: true schema: type: string format: uuid title: Artefact Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ArtefactOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/reports/digest/weekly: get: tags: - reports summary: Executive weekly digest (JSON, print-ready HTML, or PDF) description: "Build a deterministic weekly digest for the caller's tenant.\n\ \nSupported formats (``?format=``):\n\n* ``json`` — Pydantic model serialised\ \ as JSON *(default)*\n* ``html`` — Self-contained print-ready HTML page\n\ * ``pdf`` — Server-side PDF generated via WeasyPrint (requires the\n \ \ WeasyPrint native library stack; returns 503 when unavailable)" operationId: weekly_digest_api_v1_reports_digest_weekly_get security: - HTTPBearer: [] parameters: - name: format in: query required: false schema: type: string pattern: ^(json|html|pdf)$ default: json title: Format - name: period_start in: query required: false schema: anyOf: - type: string format: date-time - type: 'null' description: ISO timestamp; defaults to now-7d title: Period Start description: ISO timestamp; defaults to now-7d - name: period_end in: query required: false schema: anyOf: - type: string format: date-time - type: 'null' description: ISO timestamp; defaults to now title: Period End description: ISO timestamp; defaults to now responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/hunt-reports/from-hunt/{saved_hunt_id}: post: tags: - hunt-reports summary: From Hunt operationId: from_hunt_api_v1_hunt_reports_from_hunt__saved_hunt_id__post security: - HTTPBearer: [] parameters: - name: saved_hunt_id in: path required: true schema: type: string format: uuid title: Saved Hunt Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BuildRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/HuntReportOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/hunt-reports/from-retro-hunt/{sighting_id}: post: tags: - hunt-reports summary: From Retro Hunt operationId: from_retro_hunt_api_v1_hunt_reports_from_retro_hunt__sighting_id__post security: - HTTPBearer: [] parameters: - name: sighting_id in: path required: true schema: type: string format: uuid title: Sighting Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BuildRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/HuntReportOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/hunt-reports: get: tags: - hunt-reports summary: List Reports operationId: list_reports_api_v1_hunt_reports_get security: - HTTPBearer: [] parameters: - name: case_id in: query required: false schema: anyOf: - type: string format: uuid - type: 'null' title: Case 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: array items: $ref: '#/components/schemas/HuntReportOut' title: Response List Reports Api V1 Hunt Reports Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/hunt-reports/{report_id}: get: tags: - hunt-reports summary: Get Report operationId: get_report_api_v1_hunt_reports__report_id__get security: - HTTPBearer: [] parameters: - name: report_id in: path required: true schema: type: string format: uuid title: Report Id - name: format in: query required: false schema: type: string pattern: ^(json|html|pdf)$ default: json title: Format responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/retro-hunts/settings: get: tags: - retro_hunts summary: This tenant's retro-hunt opt-in and budget operationId: get_retro_hunt_settings_api_v1_retro_hunts_settings_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RetroHuntSettingsOut' security: - HTTPBearer: [] put: tags: - retro_hunts summary: Opt this tenant in or out of retro-hunt sweeps description: 'Write the tenant''s half of the switch. The budget columns are deliberately not writable here. They are the operator''s ceiling on what one tenant can cost the deployment, and a tenant that could raise its own ceiling would not have one.' operationId: put_retro_hunt_settings_api_v1_retro_hunts_settings_put requestBody: content: application/json: schema: $ref: '#/components/schemas/RetroHuntSettingsIn' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RetroHuntSettingsOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/report-builder/validate: post: tags: - report_builder summary: Validate Report description: 'Validate a report definition (whitelisted widget types + sources, unique ids, bounded size) without rendering or persisting it.' operationId: validate_report_api_v1_report_builder_validate_post requestBody: content: application/json: schema: $ref: '#/components/schemas/ReportDefinitionBody' required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Validate Report Api V1 Report Builder Validate Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/report-builder/render: post: tags: - report_builder summary: Render description: 'Render a report by resolving each widget''s data server-side. Wires the real ``alerts_by_severity`` resolver (tenant-scoped) + ``static``; other sources render an honest ``error`` until their resolver is wired.' operationId: render_api_v1_report_builder_render_post requestBody: content: application/json: schema: $ref: '#/components/schemas/ReportDefinitionBody' required: true responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Render Api V1 Report Builder Render Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/airgap/status: get: tags: - airgap summary: Current air-gap egress policy description: "Return the live air-gap policy snapshot for this pod.\n\nResponse\ \ shape::\n\n {\n \"enabled\": false,\n \"allowlist\": [\"\ mirror.example.com\"],\n \"implicit_private_suffixes\": [\".local\"\ , \".internal\", ...],\n \"policy\": \"Air-gapped mode is OFF — outbound\ \ HTTP is unrestricted.\"\n }\n\nThe ``policy`` field is a human-readable\ \ summary suitable for\nembedding in audit reports." operationId: get_airgap_status_api_v1_airgap_status_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Get Airgap Status Api V1 Airgap Status Get security: - HTTPBearer: [] /api/v1/llm/status: get: tags: - llm summary: Current LLM provider configuration description: "Return the env-only LLM provider snapshot for this pod.\n\n**Authenticated**\ \ -- the signature binds ``AuthUser`` and has for as long\nas this route has\ \ existed. The docstring used to say \"intentionally\nunauthenticated\", which\ \ was prose disagreeing with the code beneath it.\n\n**Env-only**: the response\ \ carries no secrets (only ``key_set: bool``).\n\nTenant-aware callers (the\ \ web UI's \"Deployment & AI\" Settings\npanel, the agents service's explain\ \ path) layer per-tenant BYOK\noverrides on top of this baseline by either:\n\ \n* calling :func:`tenant_llm_status` with their own DB session, or\n* pairing\ \ this response with ``GET /api/v1/llm/credentials`` to\n perform the merge\ \ client-side." operationId: get_llm_status_api_v1_llm_status_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Get Llm Status Api V1 Llm Status Get security: - HTTPBearer: [] /api/v1/llm/runtime: get: tags: - llm summary: Where the local model is actually running description: 'Ask Ollama whether the model is on a GPU or a CPU, and say so. Separate from ``/status`` deliberately. That route reports *configuration* -- which provider and model this pod is pointed at. This one reports *outcome*, which can differ: a GPU reservation is a request, and a model can still land on the CPU for want of VRAM or a usable driver. Reading the compose file back would report the intent and call it the result. Carries the commands that would change the answer, because the console cannot change it: switching the bundled Ollama onto a GPU means restarting that container with different compose arguments, which a web service must not be able to do.' operationId: get_llm_runtime_api_v1_llm_runtime_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Get Llm Runtime Api V1 Llm Runtime Get security: - HTTPBearer: [] /api/v1/llm/credentials: get: tags: - llm summary: Read the tenant's BYOK LLM credential description: 'Return the per-tenant LLM credential, or ``null`` if none stored. Returning ``null`` (rather than 404) on the empty case lets the UI render a single "Configure your LLM" CTA without dispatching on error codes. The Settings panel renders a "no credential set — falling back to platform defaults" hint when this is null.' operationId: get_llm_credential_api_v1_llm_credentials_get responses: '200': description: Successful Response content: application/json: schema: anyOf: - $ref: '#/components/schemas/LlmCredentialView' - type: 'null' title: Response Get Llm Credential Api V1 Llm Credentials Get security: - HTTPBearer: [] put: tags: - llm summary: Upsert the tenant's BYOK LLM credential description: "Create or update the tenant's BYOK credential.\n\nBehaviour notes:\n\ \n* ``api_key`` is required for hosted SaaS providers on first write.\n A\ \ subsequent PUT with ``api_key=null`` keeps the stored\n ciphertext (useful\ \ when toggling ``enabled`` or changing\n ``model`` without re-typing the\ \ secret).\n* Whenever ``api_key`` is provided we bump ``last_rotated_at``\ \ so\n the UI can render \"rotated 3 days ago\".\n* The audit row's ``changes``\ \ payload includes the *fact* of a\n rotation but never the key itself —\ \ we only record whether the\n key was changed, the previous + new providers,\ \ and whether the\n row is enabled." operationId: upsert_llm_credential_api_v1_llm_credentials_put requestBody: content: application/json: schema: $ref: '#/components/schemas/LlmCredentialUpsert' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/LlmCredentialView' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] delete: tags: - llm summary: Remove the tenant's BYOK LLM credential description: Hard-delete the credential. Platform falls back to env-var config. operationId: delete_llm_credential_api_v1_llm_credentials_delete responses: '204': description: Successful Response security: - HTTPBearer: [] /api/v1/llm/credentials/test: post: tags: - llm summary: Make one real call with this tenant's credential description: 'Place one minimal completion and report what happened. There was no way to find out whether a saved key worked. The three routes above validate *shape* -- that the URL parses, that the provider/key/base combination is internally consistent -- and none of them talks to the provider, so an operator who pasted a revoked key learned about it when triage silently fell back to the deterministic path. That failure is quiet by design, which is exactly what makes it hard to attribute. `settings:write` rather than `settings:read`: this spends the tenant''s money and touches an external service, which is not a read.' operationId: test_llm_credential_api_v1_llm_credentials_test_post responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/LlmCredentialTestResult' security: - HTTPBearer: [] /api/v1/llm/credentials/models: post: tags: - llm summary: List the chat models a custom OpenAI-compatible endpoint serves description: 'Ask ``GET {base_url}/models`` and return the chat-capable entries. For the ``custom`` provider, whose model was otherwise a hand-typed id. `settings:write`, like `/test`: this dials an operator-supplied URL with private addresses permitted, which is not something a read-only role should be able to point at the network. The stored key goes only to the stored base URL. A form that has changed the base URL and left the key blank gets a keyless request, not the saved key sent somewhere it was never configured to go; an endpoint that needs a key then answers 401 and the detail says so.' operationId: discover_llm_models_api_v1_llm_credentials_models_post requestBody: content: application/json: schema: $ref: '#/components/schemas/LlmModelDiscoveryRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/LlmModelDiscoveryResult' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/threatintel/stix/indicators: get: tags: - Threat Intelligence summary: List Indicators description: List STIX 2.1 indicators from the threat intelligence store. operationId: list_indicators_api_v1_threatintel_stix_indicators_get security: - HTTPBearer: [] 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: 200 minimum: 1 default: 25 title: Page Size - name: label in: query required: false schema: anyOf: - type: string - type: 'null' title: Label responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/IndicatorListResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - Threat Intelligence summary: Create Indicator description: Publish a new STIX 2.1 indicator and optionally mirror it to MISP. operationId: create_indicator_api_v1_threatintel_stix_indicators_post security: - HTTPBearer: [] parameters: - name: push_to_misp in: query required: false schema: anyOf: - type: boolean - type: 'null' description: Mirror this indicator to the configured MISP instance. Defaults to the value of MISP_PUSH_AUTO. title: Push To Misp description: Mirror this indicator to the configured MISP instance. Defaults to the value of MISP_PUSH_AUTO. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/STIXIndicatorCreate' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/STIXIndicatorWithPush' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/threatintel/stix/bundles: get: tags: - Threat Intelligence summary: List Bundles description: List STIX 2.1 bundles. operationId: list_bundles_api_v1_threatintel_stix_bundles_get security: - HTTPBearer: [] responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/BundleListResponse' post: tags: - Threat Intelligence summary: Create Bundle description: Create a new STIX 2.1 bundle and optionally mirror it to MISP. operationId: create_bundle_api_v1_threatintel_stix_bundles_post security: - HTTPBearer: [] parameters: - name: push_to_misp in: query required: false schema: anyOf: - type: boolean - type: 'null' description: Mirror this bundle to the configured MISP instance as a single MISP event (one attribute per translatable indicator). Defaults to MISP_PUSH_AUTO. title: Push To Misp description: Mirror this bundle to the configured MISP instance as a single MISP event (one attribute per translatable indicator). Defaults to MISP_PUSH_AUTO. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/STIXBundleCreate' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/STIXBundleWithPush' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/threatintel/stix/taxii/collections: get: tags: - Threat Intelligence summary: List Taxii Collections description: 'Demo-only sample of TAXII collection metadata. This is **not** the TAXII server. It returns a fixed sample list and is gated behind demo mode, and it predates the real implementation added for depth item 4.2, which lives at ``/api/v1/taxii2/`` and serves discovery, API root, collections, objects, manifest and ``added_after`` pagination over this tenant''s own indicator store. Kept rather than deleted because removing a published operation is a breaking change to the committed OpenAPI document; said plainly here because two surfaces both called "TAXII collections" is how a reader concludes the product has two TAXII implementations.' operationId: list_taxii_collections_api_v1_threatintel_stix_taxii_collections_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TAXIICollectionListResponse' security: - HTTPBearer: [] /api/v1/threatintel/stix/misp/health: get: tags: - Threat Intelligence - MISP push summary: Misp Push Health description: 'Check whether MISP push is configured and reachable. Surfaces enough state for an operator to debug a misconfigured deployment without leaking the API key. Calls ``/users/view/me`` against MISP only when the client is configured.' operationId: misp_push_health_api_v1_threatintel_stix_misp_health_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MispPushHealth' security: - HTTPBearer: [] /api/v1/threatintel/stix/misp/dry-run: post: tags: - Threat Intelligence - MISP push summary: Misp Push Dry Run description: 'Show the MISP event payload that *would* be pushed, without sending it. Useful for operators tuning STIX → MISP mappings, and for proving that an air-gapped deployment will refuse to send. The endpoint runs the air-gap check against the configured MISP URL and reports the result, but never opens an HTTP connection.' operationId: misp_push_dry_run_api_v1_threatintel_stix_misp_dry_run_post requestBody: content: application/json: schema: $ref: '#/components/schemas/MispDryRunRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/MispDryRunResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/deployment/config: get: tags: - Deployment summary: Get Deployment Config description: Return the current deployment configuration. operationId: get_deployment_config_api_v1_deployment_config_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/DeploymentConfig' security: - HTTPBearer: [] put: tags: - Deployment summary: Update Deployment Config description: Update deployment configuration fields. operationId: update_deployment_config_api_v1_deployment_config_put requestBody: content: application/json: schema: $ref: '#/components/schemas/DeploymentConfigUpdate' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/DeploymentConfig' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/deployment/airgap/status: get: tags: - Deployment summary: Get Airgap Status description: 'Check air-gap readiness: local LLM health, offline bundles, sync age.' operationId: get_airgap_status_api_v1_deployment_airgap_status_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AirgapStatus' security: - HTTPBearer: [] /api/v1/deployment/airgap/bundle: post: tags: - Deployment summary: Create Airgap Bundle description: Trigger creation of an offline update bundle for air-gapped deployments. operationId: create_airgap_bundle_api_v1_deployment_airgap_bundle_post responses: '202': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/BundleJob' security: - HTTPBearer: [] /api/v1/fusion/health: get: tags: - fusion summary: Fusion service health operationId: fusion_health_api_v1_fusion_health_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Fusion Health Api V1 Fusion Health Get security: - HTTPBearer: [] /api/v1/fusion/metrics: get: tags: - fusion summary: Fusion worker metrics operationId: fusion_metrics_api_v1_fusion_metrics_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Fusion Metrics Api V1 Fusion Metrics Get security: - HTTPBearer: [] /api/v1/fusion/ml/status: get: tags: - fusion summary: Fusion ML model status operationId: ml_status_api_v1_fusion_ml_status_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Ml Status Api V1 Fusion Ml Status Get /api/v1/fusion/entity-risk/queue: get: tags: - fusion summary: Top entities by risk score operationId: entity_risk_queue_api_v1_fusion_entity_risk_queue_get security: - HTTPBearer: [] parameters: - name: tenant_id in: query required: false schema: anyOf: - type: string format: uuid - type: 'null' title: Tenant Id - name: limit in: query required: false schema: type: integer maximum: 200 minimum: 1 default: 25 title: Limit - name: promoted_only in: query required: false schema: type: boolean default: false title: Promoted Only responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Entity Risk Queue Api V1 Fusion Entity Risk Queue Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/fusion/entity-risk/stats: get: tags: - fusion summary: Entity-risk queue stats operationId: entity_risk_stats_api_v1_fusion_entity_risk_stats_get security: - HTTPBearer: [] parameters: - name: tenant_id in: query required: false schema: anyOf: - type: string format: uuid - type: 'null' title: Tenant Id responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Entity Risk Stats Api V1 Fusion Entity Risk Stats Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/fusion/entity-risk/{entity_type}/{entity_value}: get: tags: - fusion summary: Entity risk record detail operationId: entity_risk_detail_api_v1_fusion_entity_risk__entity_type___entity_value__get security: - HTTPBearer: [] parameters: - name: entity_type in: path required: true schema: type: string title: Entity Type - name: entity_value in: path required: true schema: type: string title: Entity Value - name: tenant_id in: query required: false schema: anyOf: - type: string format: uuid - type: 'null' title: Tenant Id responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Entity Risk Detail Api V1 Fusion Entity Risk Entity Type Entity Value Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/agents/tools: get: tags: - agents summary: List Agent Tools description: "Return the agent's allowed tool surface for this tenant.\n\nThe\ \ pivot is **instance × effective capability**:\n\n1. Pull every enabled connector\ \ for the tenant.\n2. For each instance, look up the connector class's declared\n\ \ capabilities from the live catalog.\n3. Intersect with the per-instance\ \ ``allowed_capabilities`` column\n (``NULL`` = no downscope; ``[]`` = explicit\ \ zero verbs; non-empty\n list = exact allowlist).\n4. Emit one ``AgentToolDescriptor``\ \ per surviving (instance, capability)\n pair.\n\nResult is sorted deterministically\ \ by ``(connector_name, capability)``\nso two consecutive calls return the\ \ same ordering — important for\nagent caches keyed off tool list hashes." operationId: list_agent_tools_api_v1_agents_tools_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AgentToolsResponse' security: - HTTPBearer: [] /api/v1/agents/hunt: post: tags: - agents summary: Run Hunt description: 'Plan and run one natural-language hunt for the caller''s tenant. The tenant comes from the authenticated principal and is never read from the body. A caller-supplied tenant on a route like this is the shape that has gone wrong here before.' operationId: run_hunt_api_v1_agents_hunt_post requestBody: content: application/json: schema: $ref: '#/components/schemas/AgentHuntRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AgentHuntResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/inbox/templates: get: tags: - inbox summary: List Templates description: List the vendor templates the operator can mint inbox URLs for. operationId: list_templates_api_v1_inbox_templates_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/InboxTemplateInfo' type: array title: Response List Templates Api V1 Inbox Templates Get security: - HTTPBearer: [] /api/v1/inbox/tokens: get: tags: - inbox summary: List Tokens description: 'List the calling tenant''s inbox tokens. By default revoked rows are filtered — operators rarely want to look at expired tokens, and showing them by default makes the "rotate token" flow noisy.' operationId: list_tokens_api_v1_inbox_tokens_get security: - HTTPBearer: [] parameters: - name: include_revoked in: query required: false schema: type: boolean default: false title: Include Revoked responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/InboxTokenListItem' title: Response List Tokens Api V1 Inbox Tokens Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' post: tags: - inbox summary: Mint Token description: 'Mint a new inbox token for the calling tenant. Returns the plaintext token + absolute URL exactly once. Subsequent list/get calls return only the fingerprint.' operationId: mint_token_api_v1_inbox_tokens_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/InboxTokenCreate' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/InboxTokenResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/inbox/tokens/{token_fingerprint}/rotate: post: tags: - inbox summary: Rotate Token description: 'Mint a new token with the same template/label/hmac, revoke the old. The wizard''s "rotate" button calls this when a URL leaks. Old token is marked revoked so the ingest service rejects requests for it immediately; new plaintext token is returned for the operator to paste into the vendor''s webhook config. We accept the fingerprint (not the full token) in the path so the plaintext token never appears in logs / proxy access logs.' operationId: rotate_token_api_v1_inbox_tokens__token_fingerprint__rotate_post security: - HTTPBearer: [] parameters: - name: token_fingerprint in: path required: true schema: type: string title: Token Fingerprint responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/InboxTokenResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/inbox/tokens/{token_fingerprint}: delete: tags: - inbox summary: Revoke Token description: 'Permanently revoke an inbox token without minting a replacement. The ingest service rejects revoked tokens; we keep the row so the UI can still show "this token was revoked at X" in audit views.' operationId: revoke_token_api_v1_inbox_tokens__token_fingerprint__delete security: - HTTPBearer: [] parameters: - name: token_fingerprint in: path required: true schema: type: string title: Token Fingerprint responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/inbox/itsm/{tenant_token}/{connector_instance_id}: post: tags: - inbox-itsm summary: Inbound ITSM webhook (Jira / ServiceNow → AiSOC case) description: 'Receive a Jira / ServiceNow webhook and mirror status onto AiSOC. Public-facing — the only authenticator is the ``tenant_token`` in the URL (and an optional ``X-AiSOC-Signature`` HMAC). Rate limiting and DDoS protection are expected to live at the edge (Cloudflare / upstream LB), not here — the vendor''s source IPs are stable enough that the ops team can configure WAF rules. Always returns 200 (never 5xx) on the happy path, even when the payload doesn''t map to anything actionable. Vendor webhook implementations retry forever on non-2xx, and a stale ticket pinging a long-deleted case shouldn''t block the rest of the queue. We surface "nothing happened" via ``InboundResult.status_changed=False`` and a human-readable ``note``.' operationId: inbound_itsm_webhook_api_v1_inbox_itsm__tenant_token___connector_instance_id__post parameters: - name: tenant_token in: path required: true schema: type: string title: Tenant Token - name: connector_instance_id in: path required: true schema: type: string format: uuid title: Connector Instance Id - name: X-AiSOC-Signature in: header required: false schema: anyOf: - type: string - type: 'null' title: X-Aisoc-Signature responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/InboundResult' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/lake/sql: post: tags: - lake summary: Execute Lake Sql description: "Run a tenant-scoped SELECT against the warm tier.\n\nPipeline:\n\ \n1. Rate-limit the tenant.\n2. Rewrite the SQL — adds ``tenant_id`` predicates\ \ and clamps\n ``LIMIT``. Errors here are 400 (syntax) or 403 (forbidden).\n\ 3. Execute against ClickHouse with the configured timeout +\n resource caps.\ \ Errors here are 502 (driver/server error),\n 422 (timeout / memory limit),\ \ or 503 (driver not configured).\n4. Audit-log the call. We record the rewriter's\ \ view of the\n query (referenced tables, effective row cap) rather than\ \ the\n raw SQL, because audit logs are user-facing and we don't want\n\ \ to round-trip a malformed or PII-laden SQL string into the\n compliance\ \ UI." operationId: execute_lake_sql_api_v1_lake_sql_post requestBody: content: application/json: schema: $ref: '#/components/schemas/LakeQueryRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/LakeQueryResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/lake/estimate: post: tags: - lake summary: Estimate Lake Sql description: 'What would this query read, and where does that data live? Charged at the schema rate rather than the query rate: an estimate runs ``EXPLAIN ESTIMATE`` and two system-table reads, none of which touch tenant data. Pricing it like a scan would train analysts to skip the estimate, which is the opposite of the point.' operationId: estimate_lake_sql_api_v1_lake_estimate_post requestBody: content: application/json: schema: $ref: '#/components/schemas/LakeEstimateRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/LakeEstimateResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/lake/query-jobs: post: tags: - lake summary: Submit Lake Query Job description: 'Submit a query that is allowed to take longer than the synchronous bound. The estimate is taken once, here, and stored on the job. It is never recomputed: it is the number the analyst agreed to before the scan started, and moving it afterwards would make the record of what they were told untrue.' operationId: submit_lake_query_job_api_v1_lake_query_jobs_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LakeQueryJobRequest' responses: '202': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/LakeQueryJobResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' get: tags: - lake summary: List Lake Query Jobs operationId: list_lake_query_jobs_api_v1_lake_query_jobs_get security: - HTTPBearer: [] parameters: - name: limit in: query required: false schema: type: integer default: 50 title: Limit responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/LakeQueryJobResponse' title: Response List Lake Query Jobs Api V1 Lake Query Jobs Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/lake/query-jobs/{job_id}: get: tags: - lake summary: Get Lake Query Job description: 'One job''s state, with a live progress read while it is running. The live read is folded into the response but is **not** written back over a terminal state: a late progress row must not make a finished job look like it started again.' operationId: get_lake_query_job_api_v1_lake_query_jobs__job_id__get security: - HTTPBearer: [] parameters: - name: job_id in: path required: true schema: type: string format: uuid title: Job Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/LakeQueryJobResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/lake/query-jobs/{job_id}/cancel: post: tags: - lake summary: Cancel Lake Query Job description: 'Stop a running scan. A job that has already ended answers 409 rather than being quietly re-marked: cancelling a finished job would rewrite the record of what happened.' operationId: cancel_lake_query_job_api_v1_lake_query_jobs__job_id__cancel_post security: - HTTPBearer: [] parameters: - name: job_id in: path required: true schema: type: string format: uuid title: Job Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/LakeQueryJobResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/lake/schema: get: tags: - lake summary: Get Lake Schema description: 'Return column metadata for the lake''s allowlisted tables. Schema discovery is read-only and never returns row data, so it''s safe to grant to roles that should not be allowed to execute SELECTs (``viewer`` has ``lake:read_schema`` but not ``lake:query``). The response is also tenant-independent — everyone sees the same columns — but we still rate-limit and audit-log it so noisy clients can''t drum the endpoint into a DoS.' operationId: get_lake_schema_api_v1_lake_schema_get responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/LakeSchemaResponse' security: - HTTPBearer: [] /api/v1/waitlist/signup: post: tags: - waitlist summary: Public waitlist signup (rate-limited per IP). description: Persist a new signup, fire a Slack ping, return the entry id. operationId: signup_api_v1_waitlist_signup_post requestBody: content: application/json: schema: $ref: '#/components/schemas/WaitlistSignupRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/WaitlistSignupResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/waitlist/entries: get: tags: - waitlist summary: List waitlist entries (admin only). operationId: list_entries_api_v1_waitlist_entries_get security: - HTTPBearer: [] parameters: - name: status_filter in: query required: false schema: anyOf: - type: string - type: 'null' title: Status Filter - name: limit in: query required: false schema: type: integer default: 100 title: Limit responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/WaitlistEntriesResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/waitlist/entries/{entry_id}: patch: tags: - waitlist summary: Update a waitlist entry's status (admin only). operationId: patch_entry_api_v1_waitlist_entries__entry_id__patch security: - HTTPBearer: [] parameters: - name: entry_id in: path required: true schema: type: string format: uuid title: Entry Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/WaitlistPatchRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/WaitlistEntryWire' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/admin/tenants/provision: post: tags: - admin - tenants summary: Promote a waitlist entry into a live managed tenant. description: 'Provision a new tenant from a waitlist entry. Idempotent on the waitlist entry id: a repeat call for an already-provisioned entry returns the existing tenant + a fresh invite link (the demo seed is *not* re-run). This keeps the support team from creating duplicate tenants if they click the button twice on a flaky network.' operationId: provision_tenant_api_v1_admin_tenants_provision_post requestBody: content: application/json: schema: $ref: '#/components/schemas/TenantProvisionRequest' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TenantProvisionResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/admin/tenants: get: tags: - admin - tenants summary: List tenants for the support team. description: Read-only tenant list, sorted by creation time descending. operationId: list_tenants_api_v1_admin_tenants_get security: - HTTPBearer: [] parameters: - name: q in: query required: false schema: anyOf: - type: string - type: 'null' title: Q - name: managed_only in: query required: false schema: type: boolean default: false title: Managed Only - name: limit in: query required: false schema: type: integer default: 200 title: Limit responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/TenantListResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/realtime/ticket: post: tags: - realtime summary: Mint Realtime Ticket description: 'Mint a short-lived ticket for the realtime WS/SSE edge. Requires a valid session (``get_current_user`` 401s otherwise). The ticket''s tenant is bound server-side from the authenticated user — the client cannot request a tenant.' operationId: mint_realtime_ticket_api_v1_realtime_ticket_post responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/RealtimeTicket' security: - HTTPBearer: [] /api/v1/ai-assets: get: tags: - ai-estate summary: List Ai Assets description: The estate, newest sighting first. operationId: list_ai_assets_api_v1_ai_assets_get security: - HTTPBearer: [] parameters: - name: kind in: query required: false schema: anyOf: - type: string - type: 'null' description: Filter to one asset kind. title: Kind description: Filter to one asset kind. - name: unlinked in: query required: false schema: type: boolean description: Only assets with no person linked. default: false title: Unlinked description: Only assets with no person linked. - 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/AiAssetListResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/ai-assets/{asset_id}: get: tags: - ai-estate summary: Get Ai Asset operationId: get_ai_asset_api_v1_ai_assets__asset_id__get security: - HTTPBearer: [] parameters: - name: asset_id in: path required: true schema: type: string format: uuid title: Asset Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AiAssetModel' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/ai-assets/{asset_id}/sanction: post: tags: - ai-estate summary: Sanction Ai Asset description: 'Approve an asset, refuse it, or put it back to undecided. The risk score is recomputed here rather than left stale, through the same `score_risk` the recorder calls, because the sanction decision is one of its inputs: an asset that has just been refused and is still running is a different exposure from one nobody had looked at.' operationId: sanction_ai_asset_api_v1_ai_assets__asset_id__sanction_post security: - HTTPBearer: [] parameters: - name: asset_id in: path required: true schema: type: string format: uuid title: Asset Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SanctionRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/AiAssetModel' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/ai-assets/scan/hosted-agents: post: tags: - ai-estate summary: Scan Hosted Agents description: 'Inventory the agents configured in a provider''s control plane. These are invisible to every other discovery path: endpoint telemetry never sees them, and gateway telemetry sees their traffic without knowing what they were configured to do.' operationId: scan_hosted_agents_api_v1_ai_assets_scan_hosted_agents_post requestBody: content: application/json: schema: $ref: '#/components/schemas/HostedAgentScanRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ScanResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/ai-assets/scan/repositories: post: tags: - ai-estate summary: Scan Repositories description: 'Record the agent instructions, skills and MCP config a repository carries. Everything read here is attacker-controlled: these files are checked in, editable by anyone who can land a pull request, and read by an agent verbatim as instructions. `ai_repo_scan` sanitises and caps every excerpt and runs the prompt-injection patterns over the content at capture, storing matched pattern *names* and never the matching text — see that module''s header.' operationId: scan_repositories_api_v1_ai_assets_scan_repositories_post requestBody: content: application/json: schema: $ref: '#/components/schemas/RepositoryScanRequest' required: true responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ScanResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - HTTPBearer: [] /api/v1/ai-chat-compliance/config: get: tags: - ai-estate summary: Get Config operationId: get_config_api_v1_ai_chat_compliance_config_get security: - HTTPBearer: [] responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ConfigResponse' put: tags: - ai-estate summary: Put Config description: 'Store the tenant''s key and switches. Encryption happens before the row is written, and a vault failure is a 503 rather than a silently-plaintext row: storing the key unencrypted because the vault was unavailable is the worst outcome available here.' operationId: put_config_api_v1_ai_chat_compliance_config_put security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ConfigRequest' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/ConfigResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - ai-estate summary: Delete Config description: 'Stop collecting, and optionally remove what was collected. Disabling without purging is the default because an operator turning a feature off should not silently destroy evidence an investigation may be relying on. Purging is the explicit choice, and it is the one a data-subject request needs.' operationId: delete_config_api_v1_ai_chat_compliance_config_delete security: - HTTPBearer: [] parameters: - name: purge in: query required: false schema: type: boolean description: Also delete everything already collected for this tenant. default: false title: Purge description: Also delete everything already collected for this tenant. responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/ai-chat-compliance/events: get: tags: - ai-estate summary: List Events operationId: list_events_api_v1_ai_chat_compliance_events_get security: - HTTPBearer: [] 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/AuditEventListResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/ai-chat-compliance/content-labels: get: tags: - ai-estate summary: List Content Labels operationId: list_content_labels_api_v1_ai_chat_compliance_content_labels_get security: - HTTPBearer: [] parameters: - name: label in: query required: false schema: anyOf: - type: string - type: 'null' description: Filter to one classification label. title: Label description: Filter to one classification label. - 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/ContentLabelListResponse' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/ai-chat-compliance/collect: post: tags: - ai-estate summary: Collect Now description: 'Run one collection pass for this tenant. Explicit rather than scheduled. Every read here reaches a third party with the tenant''s own credential, and the plan''s rule for anything that reaches out is off by default and enabled per tenant — so the tenant decides when it runs, not a loop that started the moment a key was saved. A tenant with no configuration, or with collection disabled, gets a zero-count response whose `skipped` says which it was. The two are different states and a bare zero cannot tell them apart.' operationId: collect_now_api_v1_ai_chat_compliance_collect_post responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/CollectionResponse' security: - HTTPBearer: [] /api/v1/war-rooms/cases/{case_id}: post: tags: - war-rooms summary: Open For Case description: 'One action: create the channel, invite, post the header, record it. Fails loudly. The analyst pressed a button and is entitled to know when it did not work — a 202 "we''ll try" here would be the toast-only control this console is not allowed to ship.' operationId: open_for_case_api_v1_war_rooms_cases__case_id__post security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string format: uuid title: Case Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/OpenWarRoomRequest' responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/WarRoomModel' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' get: tags: - war-rooms summary: List For Case operationId: list_for_case_api_v1_war_rooms_cases__case_id__get security: - HTTPBearer: [] parameters: - name: case_id in: path required: true schema: type: string format: uuid title: Case Id responses: '200': description: Successful Response content: application/json: schema: type: array items: $ref: '#/components/schemas/WarRoomModel' title: Response List For Case Api V1 War Rooms Cases Case Id Get '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/war-rooms/{war_room_id}/sync: post: tags: - war-rooms summary: Sync operationId: sync_api_v1_war_rooms__war_room_id__sync_post security: - HTTPBearer: [] parameters: - name: war_room_id in: path required: true schema: type: string format: uuid title: War Room Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/SyncResult' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/war-rooms/{war_room_id}/close: post: tags: - war-rooms summary: Close description: 'Stop mirroring. The channel itself is left alone, deliberately. Archiving somebody''s channel from a console button is a bigger action than it looks: the transcript is often the incident''s own record and the people in it did not press anything.' operationId: close_api_v1_war_rooms__war_room_id__close_post security: - HTTPBearer: [] parameters: - name: war_room_id in: path required: true schema: type: string format: uuid title: War Room Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/WarRoomModel' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/war-rooms/{war_room_id}/decision: post: tags: - war-rooms summary: Channel Decision description: 'A decision taken in the channel, delivered by the bot. Service-token only. See the module header: the bot has verified the vendor''s signature and this service cannot, so the token is the whole trust relationship and ``CHANNEL_DECISIONS`` is its blast radius.' operationId: channel_decision_api_v1_war_rooms__war_room_id__decision_post parameters: - name: war_room_id in: path required: true schema: type: string format: uuid title: War Room Id - name: X-AiSOC-Service-Token in: header required: false schema: anyOf: - type: string - type: 'null' title: X-Aisoc-Service-Token requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ChannelDecisionRequest' responses: '200': description: Successful Response content: application/json: schema: type: object additionalProperties: true title: Response Channel Decision Api V1 War Rooms War Room Id Decision Post '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /auth/saml/login: get: tags: - auth-saml summary: Saml Login description: 'Initiate SAML SSO — redirect to the IdP this connection names. `issuer` is optional and the console does not send one: a deployment with a single enabled SAML connection has an unambiguous answer. It exists so a deployment with several can link one button per provider.' operationId: saml_login_auth_saml_login_get parameters: - name: redirect in: query required: false schema: type: string default: / title: Redirect - name: issuer in: query required: false schema: anyOf: - type: string - type: 'null' title: Issuer responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /auth/saml/acs: post: tags: - auth-saml summary: Saml Acs description: Assertion Consumer Service — process IdP POST-back and issue JWT. operationId: saml_acs_auth_saml_acs_post responses: '200': description: Successful Response content: application/json: schema: {} /auth/saml/metadata: get: tags: - auth-saml summary: Saml Metadata description: Return SP SAML metadata XML. operationId: saml_metadata_auth_saml_metadata_get responses: '200': description: Successful Response content: application/json: schema: {} /auth/saml/logout: get: tags: - auth-saml summary: Saml Logout description: 'Initiate SAML SLO, at the SingleLogoutService the connection names. Resolved the same way `/login` is. A deployment whose IdP comes from a connection has no `SAML_IDP_SLO_URL`, so this used to build a logout request against an empty endpoint and 500 on the way out of a session. An SLO that cannot be initiated is a local logout, not an error.' operationId: saml_logout_auth_saml_logout_get parameters: - name: issuer in: query required: false schema: anyOf: - type: string - type: 'null' title: Issuer responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /auth/oidc/login: get: tags: - auth-oidc summary: Oidc Login description: Initiate OIDC authorization code flow. operationId: oidc_login_auth_oidc_login_get parameters: - name: redirect in: query required: false schema: type: string default: / title: Redirect responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /auth/oidc/callback: get: tags: - auth-oidc summary: Oidc Callback description: Handle OIDC authorization code callback and issue JWT. operationId: oidc_callback_auth_oidc_callback_get parameters: - name: code in: query required: true schema: type: string title: Code - name: state in: query required: true schema: type: string title: State - name: error in: query required: false schema: anyOf: - type: string - type: 'null' title: Error responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /auth/oidc/userinfo: get: tags: - auth-oidc summary: Oidc Userinfo description: 'Proxy userinfo from upstream OIDC provider using the stored access token. Requires `Authorization: Bearer ` header — the JWT sub is used to look up the upstream token (stub: returns claims from AiSOC JWT).' operationId: oidc_userinfo_auth_oidc_userinfo_get responses: '200': description: Successful Response content: application/json: schema: {} /auth/oidc/logout: get: tags: - auth-oidc summary: Oidc Logout description: RP-initiated logout — clear cookie and redirect to provider end_session. operationId: oidc_logout_auth_oidc_logout_get parameters: - name: post_logout_redirect_uri in: query required: false schema: type: string default: / title: Post Logout Redirect Uri responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /scim/v2/ServiceProviderConfig: get: tags: - scim summary: Get Service Provider Config operationId: get_service_provider_config_scim_v2_ServiceProviderConfig_get responses: '200': description: Successful Response content: application/json: schema: {} security: - HTTPBearer: [] /scim/v2/ResourceTypes: get: tags: - scim summary: List Resource Types operationId: list_resource_types_scim_v2_ResourceTypes_get responses: '200': description: Successful Response content: application/json: schema: {} security: - HTTPBearer: [] /scim/v2/ResourceTypes/{resource_type_id}: get: tags: - scim summary: Get Resource Type operationId: get_resource_type_scim_v2_ResourceTypes__resource_type_id__get security: - HTTPBearer: [] 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/Schemas: get: tags: - scim summary: List Schemas operationId: list_schemas_scim_v2_Schemas_get responses: '200': description: Successful Response content: application/json: schema: {} security: - HTTPBearer: [] /scim/v2/Schemas/{schema_id}: get: tags: - scim summary: Get Schema operationId: get_schema_scim_v2_Schemas__schema_id__get security: - HTTPBearer: [] 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/Users: get: tags: - scim summary: List Users description: 'List or look up principals. A provider calls this with a filter before every create, so the filter path is the hot one and an unsupported filter must be an error rather than a full listing.' operationId: list_users_scim_v2_Users_get security: - HTTPBearer: [] 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: 100 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: - scim summary: Create User description: 'Create a principal, or return the existing one. A repeated create for a ``userName`` that already exists is answered 409, which is what RFC 7644 specifies and what a provider uses to reconcile. Creating a second principal for the same address instead would give one person two accounts with two sets of permissions.' operationId: create_user_scim_v2_Users_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: type: object additionalProperties: true title: Body responses: '201': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /scim/v2/Users/{user_id}: get: tags: - scim summary: Get User operationId: get_user_scim_v2_Users__user_id__get security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string title: User Id responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' put: tags: - scim summary: Replace User description: 'Replace a principal''s attributes. One provider sends PUT for ordinary profile updates and for deactivation, so this path has to handle ``active: false`` with the same consequences the PATCH path gives it.' operationId: replace_user_scim_v2_Users__user_id__put security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string title: User Id requestBody: required: true content: application/json: schema: type: object additionalProperties: true title: Body responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' patch: tags: - scim summary: Patch User description: 'Apply a PATCH, which is how both providers deactivate a principal. The body shapes differ between providers in five ways, all resolved by ``app.services.scim.patch``.' operationId: patch_user_scim_v2_Users__user_id__patch security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string title: User Id requestBody: required: true content: application/json: schema: type: object additionalProperties: true title: Body responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - scim summary: Delete User description: 'Deprovision a principal. Deactivates rather than deletes. A deleted row takes its audit attribution, case ownership and approval history with it, and this platform''s own records reference the principal. Access ends completely either way, which is what DELETE means here.' operationId: delete_user_scim_v2_Users__user_id__delete security: - HTTPBearer: [] parameters: - name: user_id in: path required: true schema: type: string title: User Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /scim/v2/Groups: get: tags: - scim summary: List Groups operationId: list_groups_scim_v2_Groups_get security: - HTTPBearer: [] 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: 100 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: - scim summary: Create Group description: 'Create a directory group and resolve what it confers. The resolved role is recorded at create time and returned in the response, so an administrator can see immediately whether the group they pushed grants anything.' operationId: create_group_scim_v2_Groups_post security: - HTTPBearer: [] requestBody: required: true content: application/json: schema: type: object additionalProperties: true title: Body responses: '201': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /scim/v2/Groups/{group_id}: get: tags: - scim summary: Get Group operationId: get_group_scim_v2_Groups__group_id__get security: - HTTPBearer: [] 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' put: tags: - scim summary: Replace Group description: 'Replace a group, including its entire membership. PUT on a group is a *set* operation: members absent from the body are removed. One provider uses it to reconcile a group wholesale.' operationId: replace_group_scim_v2_Groups__group_id__put security: - HTTPBearer: [] parameters: - name: group_id in: path required: true schema: type: string title: Group Id requestBody: required: true content: application/json: schema: type: object additionalProperties: true title: Body responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' patch: tags: - scim summary: Patch Group description: 'Apply a group PATCH, which is how membership changes arrive. Both providers express member removal two ways, and one of those puts the member id in the path filter rather than the value. Both shapes are normalised before they reach this handler.' operationId: patch_group_scim_v2_Groups__group_id__patch security: - HTTPBearer: [] parameters: - name: group_id in: path required: true schema: type: string title: Group Id requestBody: required: true content: application/json: schema: type: object additionalProperties: true title: Body responses: '200': description: Successful Response content: application/json: schema: {} '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - scim summary: Delete Group description: Delete a group and recompute what its members are left holding. operationId: delete_group_scim_v2_Groups__group_id__delete security: - HTTPBearer: [] parameters: - name: group_id in: path required: true schema: type: string title: Group Id responses: '204': description: Successful Response '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /graphql: get: tags: - graphql summary: Handle Http Get operationId: handle_http_get_graphql_get responses: '200': description: The GraphiQL integrated development environment. content: application/json: schema: {} '404': description: Not found if GraphiQL or query via GET are not enabled. security: - HTTPBearer: [] post: tags: - graphql summary: Handle Http Post operationId: handle_http_post_graphql_post responses: '200': description: Successful Response content: application/json: schema: {} security: - HTTPBearer: [] /health: get: tags: - system summary: Health Check description: 'Health check endpoint. Includes the current air-gap policy snapshot so operators can confirm zero-egress mode is engaged on this pod (Tier 3.1).' operationId: health_check_health_get responses: '200': description: Successful Response content: application/json: schema: additionalProperties: true type: object title: Response Health Check Health Get /metrics: get: tags: - system summary: Metrics description: "Prometheus metrics endpoint.\n\nAuth gate:\n\n* If ``settings.METRICS_TOKEN``\ \ is set, callers MUST present\n ``Authorization: Bearer ``.\ \ Comparison uses\n ``hmac.compare_digest`` to avoid timing leaks.\n* If\ \ ``METRICS_TOKEN`` is empty, the endpoint is open **only** in a\n development-class\ \ environment. In any other environment we refuse\n the scrape with a 401\ \ so operators don't accidentally ship an\n unauthenticated metrics endpoint\ \ to the open internet." operationId: metrics_metrics_get responses: '200': description: Successful Response content: application/json: schema: {} security: - HTTPBearer: [] components: schemas: AcknowledgeRequest: properties: note: type: string maxLength: 2000 title: Note default: '' type: object title: AcknowledgeRequest ActionCount: properties: action: type: string title: Action count: type: integer title: Count type: object required: - action - count title: ActionCount description: How many of each kind of action SOC operators took. ActionPolicy: properties: action: type: string title: Action blast_radius: type: string title: Blast Radius thresholds: $ref: '#/components/schemas/ThresholdTriple' default_thresholds: $ref: '#/components/schemas/ThresholdTriple' overridden: type: boolean title: Overridden override_source: anyOf: - type: string - type: 'null' title: Override Source last_updated_at: anyOf: - type: string - type: 'null' title: Last Updated At last_updated_by: anyOf: - type: string - type: 'null' title: Last Updated By last_reason: anyOf: - type: string - type: 'null' title: Last Reason capability: anyOf: - type: string - type: 'null' title: Capability executable: type: boolean title: Executable default: false effective_auto_execute: type: boolean title: Effective Auto Execute default: false auto_executes_at_any_tier: type: boolean title: Auto Executes At Any Tier default: false effective_tier: type: string title: Effective Tier effective_reason: type: string title: Effective Reason type: object required: - action - blast_radius - thresholds - default_thresholds - overridden - effective_tier - effective_reason title: ActionPolicy AddAlertsRequest: properties: alert_ids: items: type: string format: uuid type: array title: Alert Ids type: object required: - alert_ids title: AddAlertsRequest AddCommentRequest: properties: body: type: string minLength: 1 title: Body type: object required: - body title: AddCommentRequest AddFindingsRequest: properties: findings: items: additionalProperties: true type: object type: array minItems: 1 title: Findings false_positive_rate: anyOf: - type: number maximum: 1.0 minimum: 0.0 - type: 'null' title: False Positive Rate type: object required: - findings title: AddFindingsRequest AdminInviteWire: properties: token: type: string title: Token expires_at: type: string title: Expires At url: type: string title: Url type: object required: - token - expires_at - url title: AdminInviteWire AdminUserOut: properties: id: type: string format: uuid title: Id email: type: string title: Email username: type: string title: Username is_active: type: boolean title: Is Active role: type: string title: Role roles: items: type: string type: array title: Roles default: [] provider: type: string title: Provider default: local last_login: anyOf: - type: string format: date-time - type: 'null' title: Last Login created_at: anyOf: - type: string format: date-time - type: 'null' title: Created At effective_permissions: items: type: string type: array title: Effective Permissions default: [] type: object required: - id - email - username - is_active - role title: AdminUserOut AgentHuntRequest: properties: hypothesis: type: string maxLength: 2000 minLength: 1 title: Hypothesis type: object required: - hypothesis title: AgentHuntRequest description: 'One natural-language hypothesis to hunt against tenant data. Named `AgentHuntRequest`, not `HuntRequest`, and the reason is a published-contract one rather than a stylistic one. `detection_rules.py` already exports a `HuntRequest`, and FastAPI resolves a name collision by qualifying **both** models with their full module path. So adding a second `HuntRequest` deleted the plain `HuntRequest` schema from the spec and renamed the existing one — breaking every generated SDK client, for a route that had not changed. The openapi-breaking gate caught it.' AgentHuntResponse: properties: hypothesis: type: string title: Hypothesis checked: type: boolean title: Checked matches: items: additionalProperties: true type: object type: array title: Matches default: [] refusals: items: type: string type: array title: Refusals default: [] unavailable_reason: anyOf: - type: string - type: 'null' title: Unavailable Reason type: object required: - hypothesis - checked title: AgentHuntResponse description: 'What the hunt found, or why it could not look. `checked` and `unavailable_reason` are separate fields deliberately. A hunt that could not run is not a hunt that found nothing, and collapsing the two is how a console shows a reassuring empty result for a search that never happened.' AgentToolDescriptor: properties: name: type: string title: Name description: Stable unique identifier for this tool, formatted as '.'. The connector_instance_id rather than connector_type so multiple instances of the same connector — e.g. two CrowdStrike tenants — are addressable. connector_id: type: string title: Connector Id description: Connector instance UUID. connector_type: type: string title: Connector Type description: Catalog connector type, e.g. 'crowdstrike'. connector_name: type: string title: Connector Name description: Operator-chosen instance display name (e.g. 'CrowdStrike — prod'). category: type: string title: Category description: Catalog category, e.g. 'edr', 'siem', 'iam'. capability: type: string title: Capability description: The capability verb (one of the values from the Capability enum, e.g. 'pull_alerts', 'query_logs', 'isolate_host'). capability_group: type: string title: Capability Group description: Coarse grouping derived from CAPABILITY_GROUPS in services/connectors. Lets an agent pre-filter by intent — e.g. only consider 'CONTAIN' verbs when deciding to quarantine a host. description: type: string title: Description description: Human-readable summary of the verb, surfaced in tool prompts. input_schema: additionalProperties: true type: object title: Input Schema description: JSON-Schema for the verb's arguments. Empty by default; concrete connectors will fill this in as the per-capability call surface stabilises in subsequent workstreams. type: object required: - name - connector_id - connector_type - connector_name - category - capability - capability_group - description title: AgentToolDescriptor description: "One callable verb on one connector instance, as the agent sees\ \ it.\n\nThe descriptor is intentionally LLM-friendly:\n\n* ``name`` is unique\ \ per tenant (``{connector_id}.{capability}``) and\n stable across redeploys,\ \ so an agent's tool-choice memory still\n resolves after a redeployment.\n\ * ``description`` is human-readable and pulled from the connector\n class\ \ so updates ship with the connector code, not the agent.\n* ``input_schema``\ \ follows JSON-Schema conventions so most agent\n frameworks (LangChain,\ \ LangGraph, MCP) can drop it in unchanged." AgentToolsResponse: properties: tools: items: $ref: '#/components/schemas/AgentToolDescriptor' type: array title: Tools tool_count: type: integer title: Tool Count connector_count: type: integer title: Connector Count type: object required: - tools - tool_count - connector_count title: AgentToolsResponse description: Tenant-wide tool catalogue surfaced to the agent layer. AgreementResponse: properties: tenant_id: type: string title: Tenant Id scope_kind: type: string title: Scope Kind scope_key: type: string title: Scope Key window: $ref: '#/components/schemas/AgreementWindowModel' recent: $ref: '#/components/schemas/AgreementWindowModel' window_start: type: string title: Window Start window_end: type: string title: Window End thresholds: additionalProperties: anyOf: - type: number - type: integer type: object title: Thresholds reconciled: type: integer title: Reconciled by_alert_class: items: $ref: '#/components/schemas/AgreementScopeModel' type: array title: By Alert Class by_rule: items: $ref: '#/components/schemas/AgreementScopeModel' type: array title: By Rule by_source: items: $ref: '#/components/schemas/AgreementScopeModel' type: array title: By Source by_model: items: $ref: '#/components/schemas/AgreementScopeModel' type: array title: By Model type: object required: - tenant_id - scope_kind - scope_key - window - recent - window_start - window_end - thresholds - reconciled - by_alert_class - by_rule - by_source - by_model title: AgreementResponse AgreementScopeModel: properties: key: type: string title: Key window: $ref: '#/components/schemas/AgreementWindowModel' type: object required: - key - window title: AgreementScopeModel AgreementWindowModel: properties: resolved: type: integer title: Resolved labelled: type: integer title: Labelled unlabeled: type: integer title: Unlabeled answered: type: integer title: Answered abstained: type: integer title: Abstained agreed: type: integer title: Agreed malicious_support: type: integer title: Malicious Support malicious_caught: type: integer title: Malicious Caught agreement_rate: anyOf: - type: number - type: 'null' title: Agreement Rate malicious_recall: anyOf: - type: number - type: 'null' title: Malicious Recall abstention_rate: anyOf: - type: number - type: 'null' title: Abstention Rate type: object required: - resolved - labelled - unlabeled - answered - abstained - agreed - malicious_support - malicious_caught title: AgreementWindowModel description: 'Counts, and the rates derived from them. Every rate is optional and is ``null`` when its denominator was zero. The console renders that as "not measured". A zero here would say the agent was wrong every time, which is a different fact with a different remedy, and it is the more flattering of the two to print by accident in the abstention column and the more damning in the agreement column.' AiAssetListResponse: properties: items: items: $ref: '#/components/schemas/AiAssetModel' type: array title: Items total: type: integer title: Total observedSources: items: type: string type: array title: Observedsources type: object required: - items - total title: AiAssetListResponse AiAssetModel: properties: id: type: string title: Id kind: type: string title: Kind externalId: type: string title: Externalid displayName: anyOf: - type: string - type: 'null' title: Displayname vendor: anyOf: - type: string - type: 'null' title: Vendor discoverySource: type: string title: Discoverysource principal: anyOf: - type: string - type: 'null' title: Principal device: anyOf: - type: string - type: 'null' title: Device effectivePermissions: anyOf: - additionalProperties: true type: object - type: 'null' title: Effectivepermissions attributes: additionalProperties: true type: object title: Attributes risk: type: integer title: Risk riskReasons: items: type: string type: array title: Riskreasons sanctioned: anyOf: - type: boolean - type: 'null' title: Sanctioned firstSeen: type: string title: Firstseen lastSeen: type: string title: Lastseen observationCount: type: integer title: Observationcount type: object required: - id - kind - externalId - discoverySource - risk - firstSeen - lastSeen - observationCount title: AiAssetModel description: One asset as the console sees it. AirgapStatus: properties: ready: type: boolean title: Ready local_llm_status: type: string title: Local Llm Status local_llm_model: anyOf: - type: string - type: 'null' title: Local Llm Model offline_bundle_version: anyOf: - type: string - type: 'null' title: Offline Bundle Version last_sync: anyOf: - type: string - type: 'null' title: Last Sync detection_rules_count: type: integer title: Detection Rules Count threat_intel_age_hours: anyOf: - type: integer - type: 'null' title: Threat Intel Age Hours checks: items: additionalProperties: true type: object type: array title: Checks type: object required: - ready - local_llm_status - local_llm_model - offline_bundle_version - last_sync - detection_rules_count - threat_intel_age_hours - checks title: AirgapStatus AlertBulkCloseRequest: properties: alert_ids: anyOf: - items: type: string format: uuid type: array maxItems: 10000 - type: 'null' title: Alert Ids statuses: anyOf: - items: type: string type: array maxItems: 10 - type: 'null' title: Statuses severities: anyOf: - items: type: string type: array maxItems: 10 - type: 'null' title: Severities categories: anyOf: - items: type: string type: array maxItems: 20 - type: 'null' title: Categories connector_types: anyOf: - items: type: string type: array maxItems: 20 - type: 'null' title: Connector Types older_than: anyOf: - type: string format: date-time - type: 'null' title: Older Than close_status: type: string enum: - resolved - false_positive - closed title: Close Status default: resolved max_count: type: integer maximum: 10000.0 minimum: 1.0 title: Max Count default: 500 comment: anyOf: - type: string maxLength: 500 - type: 'null' title: Comment additionalProperties: false type: object title: AlertBulkCloseRequest description: Ids or filters — never neither, never a wildcard. AlertBulkCloseResponse: properties: matched_count: type: integer title: Matched Count closed_count: type: integer title: Closed Count close_status: type: string title: Close Status backup_table: type: string title: Backup Table type: object required: - matched_count - closed_count - close_status - backup_table title: AlertBulkCloseResponse AlertDetailResponse: properties: id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id title: type: string title: Title description: anyOf: - type: string - type: 'null' title: Description severity: type: string title: Severity status: type: string title: Status priority: type: integer title: Priority category: anyOf: - type: string - type: 'null' title: Category mitre_tactics: items: {} type: array title: Mitre Tactics mitre_techniques: items: {} type: array title: Mitre Techniques connector_type: anyOf: - type: string - type: 'null' title: Connector Type ai_score: anyOf: - type: number - type: 'null' title: Ai Score ai_summary: anyOf: - type: string - type: 'null' title: Ai Summary ai_recommendations: items: {} type: array title: Ai Recommendations confidence: anyOf: - type: integer - type: 'null' title: Confidence confidence_label: anyOf: - type: string - type: 'null' title: Confidence Label confidence_rationale: anyOf: - items: {} type: array - type: 'null' title: Confidence Rationale disposition: anyOf: - type: string - type: 'null' title: Disposition src_ip_class: anyOf: - type: string - type: 'null' title: Src Ip Class src_ip_class_dataset_version: anyOf: - type: string - type: 'null' title: Src Ip Class Dataset Version affected_ips: items: {} type: array title: Affected Ips affected_hosts: items: {} type: array title: Affected Hosts affected_users: items: {} type: array title: Affected Users case_id: anyOf: - type: string format: uuid - type: 'null' title: Case Id tags: items: {} type: array title: Tags event_time: type: string format: date-time title: Event Time first_seen: type: string format: date-time title: First Seen last_seen: type: string format: date-time title: Last Seen snoozed_until: anyOf: - type: string format: date-time - type: 'null' title: Snoozed Until snoozed_by_id: anyOf: - type: string format: uuid - type: 'null' title: Snoozed By Id created_at: type: string format: date-time title: Created At updated_at: type: string format: date-time title: Updated At narrative: anyOf: - type: string - type: 'null' title: Narrative related_entities: items: $ref: '#/components/schemas/RelatedEntity' type: array title: Related Entities default: [] mini_timeline: items: $ref: '#/components/schemas/MiniTimelineEvent' type: array title: Mini Timeline default: [] recommended_actions: items: $ref: '#/components/schemas/RecommendedAction' type: array title: Recommended Actions default: [] raw_event: additionalProperties: true type: object title: Raw Event default: {} wazuh_locator: additionalProperties: true type: object title: Wazuh Locator default: {} type: object required: - id - tenant_id - title - description - severity - status - priority - category - mitre_tactics - mitre_techniques - connector_type - ai_score - ai_summary - ai_recommendations - affected_ips - affected_hosts - affected_users - case_id - tags - event_time - first_seen - last_seen - created_at - updated_at title: AlertDetailResponse description: "Alert response enriched with everything the Investigation Rail\ \ needs.\n\nThe list endpoint stays on the smaller :class:`AlertResponse`\ \ shape\nto keep paginated payloads light. The detail endpoint promotes to\n\ this enriched shape so the rail renders without extra round-trips:\n\n* ``narrative``\ \ — deterministic correlation prose. Cached on the\n ``Alert`` row by the\ \ fusion service; lazily filled by the\n endpoint when missing (legacy rows).\ \ Always a string by the\n time it leaves the API.\n* ``related_entities``\ \ — pivotable entities grouped by\n principal / network / workflow / tenant.\n\ * ``mini_timeline`` — up to ``MAX_TIMELINE_EVENTS`` recent events\n merged\ \ from the case timeline and the audit log.\n* ``recommended_actions`` — normalised\ \ structured actions from\n the ResponderAgent (or the legacy list-of-strings\ \ shape)." AlertFacetCounts: properties: by_severity: additionalProperties: type: integer type: object title: By Severity by_status: additionalProperties: type: integer type: object title: By Status unresolved: type: integer title: Unresolved default: 0 type: object title: AlertFacetCounts description: 'Counts over the whole filtered result set, not the returned page. The console''s Critical / High / Unresolved tiles used to be `alerts.filter(...)` over the 25 rows it had loaded, rendered beside a server-side `total` in the hundreds — so the tiles were arithmetically capped at the page size and changed when the analyst paged. Computed from the same `WHERE` clause as `total` rather than over the whole tenant, so all four numbers on that strip describe one set of rows.' AlertLinkCreate: properties: alert_id: type: string format: uuid title: Alert Id node_id: type: string format: uuid title: Node Id link_reason: type: string title: Link Reason confidence: type: number title: Confidence default: 1.0 type: object required: - alert_id - node_id - link_reason title: AlertLinkCreate AlertLinkOut: properties: alert_id: type: string format: uuid title: Alert Id node_id: type: string format: uuid title: Node Id link_reason: type: string title: Link Reason confidence: type: number title: Confidence default: 1.0 id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id created_at: type: string title: Created At type: object required: - alert_id - node_id - link_reason - id - tenant_id - created_at title: AlertLinkOut AlertListResponse: properties: items: items: $ref: '#/components/schemas/AlertResponse' type: array title: Items total: type: integer title: Total page: type: integer title: Page page_size: type: integer title: Page Size pages: type: integer title: Pages facets: $ref: '#/components/schemas/AlertFacetCounts' type: object required: - items - total - page - page_size - pages title: AlertListResponse AlertMetrics: properties: total: type: integer title: Total new: type: integer title: New critical: type: integer title: Critical high: type: integer title: High medium: type: integer title: Medium low: type: integer title: Low resolvedToday: type: integer title: Resolvedtoday resolved: type: integer title: Resolved default: 0 mttr: type: number title: Mttr mttr_sample_count: type: integer title: Mttr Sample Count default: 0 type: object required: - total - new - critical - high - medium - low - resolvedToday - mttr title: AlertMetrics AlertOverrideRequest: properties: alert_id: type: string title: Alert Id original_verdict: type: string title: Original Verdict description: The AI-generated verdict being overridden corrected_verdict: type: string title: Corrected Verdict description: 'Analyst''s verdict: true_positive | benign_true_positive | false_positive | benign | escalate' reason: anyOf: - type: string - type: 'null' title: Reason description: Optional free-text justification reason_code: anyOf: - type: string - type: 'null' title: Reason Code description: Optional code from the closed disagreement vocabulary (known_admin_tool, approved_pentest, expected_service_account, known_scanner, business_application, bad_detection_logic, missing_context, true_positive_confirmed). Supplying one lets the platform compile durable organisation memory from repeated disagreement; free text alone cannot generalise. type: object required: - alert_id - original_verdict - corrected_verdict title: AlertOverrideRequest AlertOverrideResponse: properties: alert_id: type: string title: Alert Id corrected_verdict: type: string title: Corrected Verdict recorded_at: type: string title: Recorded At memory_key: anyOf: - type: string - type: 'null' title: Memory Key redisposition_candidates: items: $ref: '#/components/schemas/RedispositionCandidateModel' type: array title: Redisposition Candidates redisposition_confirmation_token: anyOf: - type: string - type: 'null' title: Redisposition Confirmation Token redisposition_capped: type: boolean title: Redisposition Capped default: false redisposition_quarantined: type: boolean title: Redisposition Quarantined default: false redisposition_total_matched: type: integer title: Redisposition Total Matched default: 0 context_statement: anyOf: - type: string - type: 'null' title: Context Statement type: object required: - alert_id - corrected_verdict - recorded_at title: AlertOverrideResponse AlertResponse: properties: id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id title: type: string title: Title description: anyOf: - type: string - type: 'null' title: Description severity: type: string title: Severity status: type: string title: Status priority: type: integer title: Priority category: anyOf: - type: string - type: 'null' title: Category mitre_tactics: items: {} type: array title: Mitre Tactics mitre_techniques: items: {} type: array title: Mitre Techniques connector_type: anyOf: - type: string - type: 'null' title: Connector Type ai_score: anyOf: - type: number - type: 'null' title: Ai Score ai_summary: anyOf: - type: string - type: 'null' title: Ai Summary ai_recommendations: items: {} type: array title: Ai Recommendations confidence: anyOf: - type: integer - type: 'null' title: Confidence confidence_label: anyOf: - type: string - type: 'null' title: Confidence Label confidence_rationale: anyOf: - items: {} type: array - type: 'null' title: Confidence Rationale disposition: anyOf: - type: string - type: 'null' title: Disposition src_ip_class: anyOf: - type: string - type: 'null' title: Src Ip Class src_ip_class_dataset_version: anyOf: - type: string - type: 'null' title: Src Ip Class Dataset Version affected_ips: items: {} type: array title: Affected Ips affected_hosts: items: {} type: array title: Affected Hosts affected_users: items: {} type: array title: Affected Users case_id: anyOf: - type: string format: uuid - type: 'null' title: Case Id tags: items: {} type: array title: Tags event_time: type: string format: date-time title: Event Time first_seen: type: string format: date-time title: First Seen last_seen: type: string format: date-time title: Last Seen snoozed_until: anyOf: - type: string format: date-time - type: 'null' title: Snoozed Until snoozed_by_id: anyOf: - type: string format: uuid - type: 'null' title: Snoozed By Id created_at: type: string format: date-time title: Created At updated_at: type: string format: date-time title: Updated At type: object required: - id - tenant_id - title - description - severity - status - priority - category - mitre_tactics - mitre_techniques - connector_type - ai_score - ai_summary - ai_recommendations - affected_ips - affected_hosts - affected_users - case_id - tags - event_time - first_seen - last_seen - created_at - updated_at title: AlertResponse AlertSnoozeRequest: properties: duration_minutes: anyOf: - type: integer - type: 'null' title: Duration Minutes until: anyOf: - type: string format: date-time - type: 'null' title: Until reason: anyOf: - type: string - type: 'null' title: Reason type: object title: AlertSnoozeRequest description: Snooze an alert from the mobile responder PWA. AlertStatsResponse: properties: total: type: integer title: Total by_severity: additionalProperties: type: integer type: object title: By Severity by_status: additionalProperties: type: integer type: object title: By Status new_last_24h: type: integer title: New Last 24H critical_open: type: integer title: Critical Open type: object required: - total - by_severity - by_status - new_last_24h - critical_open title: AlertStatsResponse AlertSubmitRequest: properties: events: items: additionalProperties: true type: object type: array title: Events default: [] connector_id: anyOf: - type: string - type: 'null' title: Connector Id connector_type: anyOf: - type: string - type: 'null' title: Connector Type source_format: anyOf: - type: string - type: 'null' title: Source Format title: anyOf: - type: string - type: 'null' title: Title description: anyOf: - type: string - type: 'null' title: Description severity: anyOf: - type: string - type: 'null' title: Severity tags: anyOf: - items: type: string type: array - type: 'null' title: Tags type: object title: AlertSubmitRequest description: "Direct-write alert submission payload.\n\nThe founder-flow demo\ \ (`aisoc submit `) and any operator who wants\nto land a hand-crafted\ \ alert on the local dev stack without a connector\nPOST one of these. The\ \ body intentionally mirrors the ingest envelope\nused by real connectors\ \ so the same fixture works against either path:\n\n { connector_id, connector_type,\ \ source_format, events: [...] }\n\nThe single payload synthesises one ``Alert``\ \ row, written directly into\nthe tenant's database, so the result is visible\ \ in\n``GET /api/v1/alerts`` and the web console within the same second.\n\ This deliberately bypasses the Kafka detection/correlation pipeline\n(which\ \ fresh clones don't run by default) so the documented \"alert in\nseconds\"\ \ promise in the quickstart actually holds." AlertUpdateRequest: properties: status: anyOf: - type: string - type: 'null' title: Status priority: anyOf: - type: integer - type: 'null' title: Priority tags: anyOf: - items: type: string type: array - type: 'null' title: Tags assignee: anyOf: - type: string format: uuid - type: 'null' title: Assignee case_id: anyOf: - type: string format: uuid - type: 'null' title: Case Id additionalProperties: false type: object title: AlertUpdateRequest ApiKeyOut: properties: id: type: string format: uuid title: Id name: type: string title: Name prefix: type: string title: Prefix scopes: items: type: string type: array title: Scopes is_active: type: boolean title: Is Active 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 created_at: type: string format: date-time title: Created At type: object required: - id - name - prefix - scopes - is_active - expires_at - last_used_at - created_at title: ApiKeyOut ApplyTuningRequest: properties: action: type: string enum: - raise_threshold - add_suppression - disable - acknowledge title: Action note: anyOf: - type: string - type: 'null' title: Note threshold: anyOf: - type: integer maximum: 1000000.0 minimum: 1.0 - type: 'null' title: Threshold description: Override threshold to set when action='raise_threshold'. suppression_reason: anyOf: - type: string maxLength: 255 - type: 'null' title: Suppression Reason description: Free-text reason recorded with the suppression placeholder. type: object required: - action title: ApplyTuningRequest description: Body for ``POST /detection/tuning/{rule_id}/apply``. ApprovalCreateRequest: properties: run_id: anyOf: - type: string format: uuid - type: 'null' title: Run Id case_id: anyOf: - type: string maxLength: 200 - type: 'null' title: Case Id alert_id: anyOf: - type: string format: uuid - type: 'null' title: Alert Id requested_by: type: string maxLength: 120 title: Requested By default: agent required_user_id: anyOf: - type: string format: uuid - type: 'null' title: Required User Id required_topic: anyOf: - type: string maxLength: 80 - type: 'null' title: Required Topic title: type: string maxLength: 200 minLength: 1 title: Title summary: type: string minLength: 1 title: Summary risk_level: type: string enum: - low - medium - high - critical title: Risk Level default: medium action: additionalProperties: true type: object title: Action expires_at: anyOf: - type: string format: date-time - type: 'null' title: Expires At type: object required: - title - summary title: ApprovalCreateRequest ApprovalDecisionRequest: properties: decision: type: string enum: - approve - deny title: Decision comment: anyOf: - type: string maxLength: 2000 - type: 'null' title: Comment type: object required: - decision title: ApprovalDecisionRequest ApprovalListResponse: properties: items: items: $ref: '#/components/schemas/ApprovalResponse' type: array title: Items total: type: integer title: Total page: type: integer title: Page page_size: type: integer title: Page Size pages: type: integer title: Pages type: object required: - items - total - page - page_size - pages title: ApprovalListResponse ApprovalResponse: properties: id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id run_id: anyOf: - type: string format: uuid - type: 'null' title: Run Id case_id: anyOf: - type: string - type: 'null' title: Case Id alert_id: anyOf: - type: string format: uuid - type: 'null' title: Alert Id requested_by: type: string title: Requested By required_user_id: anyOf: - type: string format: uuid - type: 'null' title: Required User Id required_topic: anyOf: - type: string - type: 'null' title: Required Topic title: type: string title: Title summary: type: string title: Summary risk_level: type: string title: Risk Level action: additionalProperties: true type: object title: Action status: type: string title: Status decided_by_id: anyOf: - type: string format: uuid - type: 'null' title: Decided By Id decided_at: anyOf: - type: string format: date-time - type: 'null' title: Decided At decision_comment: anyOf: - type: string - type: 'null' title: Decision Comment expires_at: anyOf: - type: string format: date-time - type: 'null' title: Expires At created_at: type: string format: date-time title: Created At updated_at: type: string format: date-time title: Updated At type: object required: - id - tenant_id - run_id - case_id - alert_id - requested_by - required_user_id - required_topic - title - summary - risk_level - action - status - decided_by_id - decided_at - decision_comment - expires_at - created_at - updated_at title: ApprovalResponse ArtefactOut: properties: id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id template_id: anyOf: - type: string format: uuid - type: 'null' title: Template Id report_type: type: string title: Report Type title: type: string title: Title period_start: type: string title: Period Start period_end: type: string title: Period End output_format: type: string title: Output Format file_size_bytes: anyOf: - type: integer - type: 'null' title: File Size Bytes delivered_to: anyOf: - items: type: string type: array - type: 'null' title: Delivered To delivered_at: anyOf: - type: string - type: 'null' title: Delivered At generated_by: type: string title: Generated By status: type: string title: Status error_message: anyOf: - type: string - type: 'null' title: Error Message created_at: type: string title: Created At type: object required: - id - tenant_id - template_id - report_type - title - period_start - period_end - output_format - file_size_bytes - delivered_to - delivered_at - generated_by - status - error_message - created_at title: ArtefactOut ArtifactDetail: properties: id: type: string format: uuid title: Id kind: type: string title: Kind sha256: type: string title: Sha256 size_bytes: type: integer title: Size Bytes event_id: anyOf: - type: string format: uuid - type: 'null' title: Event Id created_at: type: string format: date-time title: Created At content: anyOf: - type: string - type: 'null' title: Content blob_ref: anyOf: - type: string - type: 'null' title: Blob Ref type: object required: - id - kind - sha256 - size_bytes - event_id - created_at - content - blob_ref title: ArtifactDetail ArtifactSummary: properties: id: type: string format: uuid title: Id kind: type: string title: Kind sha256: type: string title: Sha256 size_bytes: type: integer title: Size Bytes event_id: anyOf: - type: string format: uuid - type: 'null' title: Event Id created_at: type: string format: date-time title: Created At type: object required: - id - kind - sha256 - size_bytes - event_id - created_at title: ArtifactSummary AssetCreate: properties: asset_type: type: string title: Asset Type default: host name: type: string title: Name fqdn: anyOf: - type: string - type: 'null' title: Fqdn ip_addresses: anyOf: - items: type: string type: array - type: 'null' title: Ip Addresses cloud_provider: anyOf: - type: string - type: 'null' title: Cloud Provider cloud_region: anyOf: - type: string - type: 'null' title: Cloud Region cloud_account: anyOf: - type: string - type: 'null' title: Cloud Account os: anyOf: - type: string - type: 'null' title: Os os_version: anyOf: - type: string - type: 'null' title: Os Version criticality: type: string title: Criticality default: medium tags: anyOf: - items: type: string type: array - type: 'null' title: Tags owner_email: anyOf: - type: string - type: 'null' title: Owner Email metadata: additionalProperties: true type: object title: Metadata type: object required: - name title: AssetCreate AssetOut: properties: asset_type: type: string title: Asset Type default: host name: type: string title: Name fqdn: anyOf: - type: string - type: 'null' title: Fqdn ip_addresses: anyOf: - items: type: string type: array - type: 'null' title: Ip Addresses cloud_provider: anyOf: - type: string - type: 'null' title: Cloud Provider cloud_region: anyOf: - type: string - type: 'null' title: Cloud Region cloud_account: anyOf: - type: string - type: 'null' title: Cloud Account os: anyOf: - type: string - type: 'null' title: Os os_version: anyOf: - type: string - type: 'null' title: Os Version criticality: type: string title: Criticality default: medium tags: anyOf: - items: type: string type: array - type: 'null' title: Tags owner_email: anyOf: - type: string - type: 'null' title: Owner Email metadata: additionalProperties: true type: object title: Metadata id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id created_at: type: string title: Created At updated_at: type: string title: Updated At last_seen: type: string title: Last Seen first_seen: type: string title: First Seen asset_metadata: additionalProperties: true type: object title: Asset Metadata type: object required: - name - id - tenant_id - created_at - updated_at - last_seen - first_seen title: AssetOut AssetUpdate: properties: asset_type: type: string title: Asset Type default: host name: type: string title: Name fqdn: anyOf: - type: string - type: 'null' title: Fqdn ip_addresses: anyOf: - items: type: string type: array - type: 'null' title: Ip Addresses cloud_provider: anyOf: - type: string - type: 'null' title: Cloud Provider cloud_region: anyOf: - type: string - type: 'null' title: Cloud Region cloud_account: anyOf: - type: string - type: 'null' title: Cloud Account os: anyOf: - type: string - type: 'null' title: Os os_version: anyOf: - type: string - type: 'null' title: Os Version criticality: type: string title: Criticality default: medium tags: anyOf: - items: type: string type: array - type: 'null' title: Tags owner_email: anyOf: - type: string - type: 'null' title: Owner Email metadata: additionalProperties: true type: object title: Metadata type: object required: - name title: AssetUpdate AttackHeatmapCell: properties: tactic: type: string title: Tactic technique: type: string title: Technique count: type: integer title: Count type: object required: - tactic - technique - count title: AttackHeatmapCell AttackPathResponse: properties: case_id: type: string title: Case Id nodes: items: $ref: '#/components/schemas/GraphNode' type: array title: Nodes edges: items: $ref: '#/components/schemas/GraphEdge' type: array title: Edges node_count: type: integer title: Node Count edge_count: type: integer title: Edge Count type: object required: - case_id - nodes - edges - node_count - edge_count title: AttackPathResponse AuditEventListResponse: properties: items: items: $ref: '#/components/schemas/AuditEventModel' type: array title: Items total: type: integer title: Total collectionEnabled: type: boolean title: Collectionenabled type: object required: - items - total - collectionEnabled title: AuditEventListResponse AuditEventModel: properties: id: type: string title: Id eventType: type: string title: Eventtype occurredAt: type: string title: Occurredat actorEmail: anyOf: - type: string - type: 'null' title: Actoremail actorIp: anyOf: - type: string - type: 'null' title: Actorip chatExternalId: anyOf: - type: string - type: 'null' title: Chatexternalid projectExternalId: anyOf: - type: string - type: 'null' title: Projectexternalid type: object required: - id - eventType - occurredAt title: AuditEventModel AuditEventOut: properties: id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id actor_id: anyOf: - type: string format: uuid - type: 'null' title: Actor Id actor_email: anyOf: - type: string - type: 'null' title: Actor Email actor_ip: anyOf: - type: string - type: 'null' title: Actor Ip action: type: string title: Action resource: anyOf: - type: string - type: 'null' title: Resource resource_id: anyOf: - type: string - type: 'null' title: Resource Id changes: anyOf: - additionalProperties: true type: object - type: 'null' title: Changes created_at: type: string title: Created At type: object required: - id - tenant_id - actor_id - actor_email - actor_ip - action - resource - resource_id - changes - created_at title: AuditEventOut AuditListResponse: properties: items: items: $ref: '#/components/schemas/AuditEventOut' type: array title: Items total: type: integer title: Total page: type: integer title: Page page_size: type: integer title: Page Size total_pages: type: integer title: Total Pages type: object required: - items - total - page - page_size - total_pages title: AuditListResponse AuthenticateBeginRequest: properties: email: anyOf: - type: string format: email - type: 'null' title: Email type: object title: AuthenticateBeginRequest AuthenticateFinishResponse: 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 default: 1800 type: object required: - access_token - refresh_token title: AuthenticateFinishResponse AutoSuggestResponse: properties: window_days: type: integer title: Window Days records_considered: type: integer title: Records Considered suggestions: items: additionalProperties: true type: object type: array title: Suggestions proposals_created: type: integer title: Proposals Created type: object required: - window_days - records_considered - suggestions - proposals_created title: AutoSuggestResponse description: Disposition-history tuning suggestions + how many became draft proposals. AutoTuneRequest: properties: enabled: type: boolean title: Enabled type: object required: - enabled title: AutoTuneRequest description: Body for ``POST /detection/tuning/{rule_id}/auto_tune``. AutonomyPolicyResponse: properties: tenant_id: type: string title: Tenant Id actions: items: $ref: '#/components/schemas/ActionPolicy' type: array title: Actions effective: $ref: '#/components/schemas/EffectiveAutonomy' type: object required: - tenant_id - actions - effective title: AutonomyPolicyResponse AvailableTools: properties: builtin: items: type: string type: array title: Builtin customer: items: type: string type: array title: Customer mcp: items: type: string type: array title: Mcp customer_unknown: type: boolean title: Customer Unknown default: false customer_unknown_reason: type: string title: Customer Unknown Reason default: '' type: object title: AvailableTools description: 'What an author may name in ``expected_pivots`` on this tenant today. Returned with the list so the editor can offer the vocabulary rather than making the author discover it by being refused. ``customer_unknown`` is part of the contract: an editor that read an unreachable action registry as "this tenant has no products" would grey out tools the tenant has.' BacktestProposalRequest: properties: window_days: type: integer maximum: 365.0 minimum: 1.0 title: Window Days default: 30 limit: type: integer maximum: 100000.0 minimum: 1.0 title: Limit default: 5000 source: anyOf: - type: string - type: 'null' title: Source description: Optional connector_type filter. type: object title: BacktestProposalRequest description: Backtest a candidate proposal's rule over historical lake events (W2.2). BacktestRequest: properties: window_days: type: integer maximum: 365.0 minimum: 1.0 title: Window Days description: How many days of history to scan. default: 30 limit: type: integer maximum: 100000.0 minimum: 1.0 title: Limit description: Max events to scan. default: 5000 source: anyOf: - type: string - type: 'null' title: Source description: Optional connector_type filter (e.g. 'okta_system_log'). type: object title: BacktestRequest description: Backtest a rule over historical lake events. BacktestResponse: properties: rule_id: type: string format: uuid title: Rule Id rule_language: type: string title: Rule Language window_days: type: integer title: Window Days events_scanned: type: integer title: Events Scanned would_fire: type: integer title: Would Fire hit_rate: type: number title: Hit Rate sample_matches: items: additionalProperties: true type: object type: array title: Sample Matches error: anyOf: - type: string - type: 'null' title: Error type: object required: - rule_id - rule_language - window_days - events_scanned - would_fire - hit_rate - sample_matches title: BacktestResponse BaselineResponse: properties: id: type: string format: uuid title: Id tenant_id: anyOf: - type: string format: uuid - type: 'null' title: Tenant Id suite: type: string title: Suite score: type: number title: Score payload: additionalProperties: true type: object title: Payload is_active: type: boolean title: Is Active recorded_by_id: anyOf: - type: string format: uuid - type: 'null' title: Recorded By Id created_at: type: string format: date-time title: Created At type: object required: - id - tenant_id - suite - score - payload - is_active - recorded_by_id - created_at title: BaselineResponse BlastRadiusResponse: properties: entity_id: type: string title: Entity Id entity_type: type: string title: Entity Type hops: type: integer title: Hops affected_nodes: items: $ref: '#/components/schemas/GraphNode' type: array title: Affected Nodes total_affected: type: integer title: Total Affected type_breakdown: additionalProperties: type: integer type: object title: Type Breakdown blast_radius_score: type: number title: Blast Radius Score type: object required: - entity_id - entity_type - hops - affected_nodes - total_affected - type_breakdown - blast_radius_score title: BlastRadiusResponse Body_replace_role_permissions_api_v1_rbac_roles__role_id__permissions_put: properties: body: anyOf: - $ref: '#/components/schemas/PermissionNamesIn' - type: 'null' raw: title: Raw type: object title: Body_replace_role_permissions_api_v1_rbac_roles__role_id__permissions_put Body_upload_asset_api_v1_branding_assets__kind__post: properties: file: type: string contentMediaType: application/octet-stream title: File type: object required: - file title: Body_upload_asset_api_v1_branding_assets__kind__post BrandingIn: properties: product_name: anyOf: - type: string maxLength: 80 minLength: 1 - type: 'null' title: Product Name primary_color: anyOf: - type: string pattern: ^#[0-9A-Fa-f]{6}$ - type: 'null' title: Primary Color accent_color: anyOf: - type: string pattern: ^#[0-9A-Fa-f]{6}$ - type: 'null' title: Accent Color support_email: anyOf: - type: string maxLength: 255 - type: 'null' title: Support Email support_url: anyOf: - type: string maxLength: 500 - type: 'null' title: Support Url sender_name: anyOf: - type: string maxLength: 80 minLength: 1 - type: 'null' title: Sender Name footer_text: anyOf: - type: string maxLength: 300 - type: 'null' title: Footer Text type: object title: BrandingIn BuildRequest: properties: case_id: anyOf: - type: string format: uuid - type: 'null' title: Case Id type: object title: BuildRequest BuildTimelineRequest: properties: identity_kind: type: string enum: - user - device - service_account - ip title: Identity Kind description: Type of identity anchor. identity_value: type: string minLength: 1 title: Identity Value description: Identity value (e.g. username, hostname). from_ts: anyOf: - type: string format: date-time - type: 'null' title: From Ts description: Start of window (defaults to 72 h ago). to_ts: anyOf: - type: string format: date-time - type: 'null' title: To Ts description: End of window (defaults to now). max_events: type: integer maximum: 2000.0 minimum: 1.0 title: Max Events default: 200 type: object required: - identity_kind - identity_value title: BuildTimelineRequest BulkToggleBody: properties: ruleIds: items: type: string type: array minItems: 1 title: Ruleids enabled: type: boolean title: Enabled type: object required: - enabled title: BulkToggleBody description: 'Bulk enable/disable payload from the analyst console. ``ruleIds`` are accepted as plain strings so the frontend can pass the same IDs it already renders from ``GET /rules`` without parsing UUIDs.' BulkToggleResponse: properties: updated: type: integer title: Updated skipped: items: type: string type: array title: Skipped type: object required: - updated - skipped title: BulkToggleResponse BulkTriageRequest: properties: alert_ids: items: type: string format: uuid type: array maxItems: 5000 minItems: 1 title: Alert Ids set_status: anyOf: - type: string - type: 'null' title: Set Status set_severity: anyOf: - type: string - type: 'null' title: Set Severity assign_to: anyOf: - type: string format: uuid - type: 'null' title: Assign To unassign: type: boolean title: Unassign default: false max_count: type: integer maximum: 5000.0 minimum: 1.0 title: Max Count default: 100 comment: type: string maxLength: 500 title: Comment default: '' type: object required: - alert_ids title: BulkTriageRequest BulkTriageResponse: properties: matched_count: type: integer title: Matched Count updated_count: type: integer title: Updated Count changes: additionalProperties: true type: object title: Changes type: object required: - matched_count - updated_count - changes title: BulkTriageResponse BundleJob: properties: job_id: type: string title: Job Id status: type: string title: Status created_at: type: string title: Created At estimated_size_mb: type: integer title: Estimated Size Mb includes: items: type: string type: array title: Includes type: object required: - job_id - status - created_at - estimated_size_mb - includes title: BundleJob BundleListResponse: properties: items: items: $ref: '#/components/schemas/STIXBundle' type: array title: Items total: type: integer title: Total type: object required: - items - total title: BundleListResponse ByokSavings: properties: is_byok_active: type: boolean title: Is Byok Active provider: type: string title: Provider recorded_cost_usd: type: number title: Recorded Cost Usd recorded_is_measured: type: boolean title: Recorded Is Measured imputed_public_cost_usd: type: number title: Imputed Public Cost Usd imputed_is_estimable: type: boolean title: Imputed Is Estimable default: false unpriced_tokens: type: integer title: Unpriced Tokens default: 0 savings_usd: type: number title: Savings Usd type: object required: - is_byok_active - provider - recorded_cost_usd - recorded_is_measured - imputed_public_cost_usd - savings_usd title: ByokSavings description: 'Imputed savings vs hosted pricing — an estimate, labelled everywhere. ``is_byok_active`` is True when the live LLM provider (per ``/llm/status``) is loopback or private — i.e. the operator is actually running their own model. When False, the savings are still computed (so the UI can show "if you switched to BYOK, you''d save ~X") but should be labelled "potential savings" in the UI. ``imputed_is_estimable`` is False when no row in the window names a model with a public list price, and then ``imputed_public_cost_usd`` and ``savings_usd`` are ``0.0`` because there is nothing to compute, not because the answer is zero. They used to be real-looking numbers in that case, built from a default rate applied to a gateway alias, and the BYOK panel announced savings against it — an invented saving on invented spend. ``unpriced_tokens`` says how much of the window the estimate omits.' CalibrationBucket: properties: predicted_lower: type: number title: Predicted Lower predicted_upper: type: number title: Predicted Upper sample_count: type: integer title: Sample Count actual_tp_rate: type: number title: Actual Tp Rate type: object required: - predicted_lower - predicted_upper - sample_count - actual_tp_rate title: CalibrationBucket description: 'One bucket of the agent confidence reliability curve. `predicted_lower` and `predicted_upper` define the AI-confidence range (e.g., 0.8–1.0). `actual_tp_rate` is the observed true-positive rate among analyst-dispositioned alerts whose ai_score landed in that bucket — i.e., the empirical hit-rate the model claimed.' CapabilitiesResponse: properties: connectors: items: $ref: '#/components/schemas/ReplayableConnectorInfo' type: array title: Connectors default_window_days: type: integer title: Default Window Days default_train_fraction: type: number title: Default Train Fraction default_bootstrap_seed: type: integer title: Default Bootstrap Seed default_bootstrap_resamples: type: integer title: Default Bootstrap Resamples min_malicious_for_headline: type: integer title: Min Malicious For Headline max_findings: type: integer title: Max Findings type: object required: - connectors - default_window_days - default_train_fraction - default_bootstrap_seed - default_bootstrap_resamples - min_malicious_for_headline - max_findings title: CapabilitiesResponse description: 'What this deployment can replay, so the console offers nothing it cannot. ``min_malicious_for_headline`` travels to the client so the page can say why a headline is withheld before a run is started, rather than only after.' CaseMetrics: properties: open: type: integer title: Open inProgress: type: integer title: Inprogress resolvedThisWeek: type: integer title: Resolvedthisweek type: object required: - open - inProgress - resolvedThisWeek title: CaseMetrics CaseResponse: properties: id: type: string format: uuid title: Id case_number: anyOf: - type: string - type: 'null' title: Case Number title: type: string title: Title description: anyOf: - type: string - type: 'null' title: Description severity: type: string title: Severity status: type: string title: Status assignee: anyOf: - type: string - type: 'null' title: Assignee mitre_techniques: items: type: string type: array title: Mitre Techniques alert_ids: items: type: string format: uuid type: array title: Alert Ids observable_graph: additionalProperties: true type: object title: Observable Graph evidence_chain: items: additionalProperties: true type: object type: array title: Evidence Chain compliance_frameworks: items: type: string type: array title: Compliance Frameworks opened_at: type: string format: date-time title: Opened At triaged_at: anyOf: - type: string format: date-time - type: 'null' title: Triaged At resolved_at: anyOf: - type: string format: date-time - type: 'null' title: Resolved At closed_at: anyOf: - type: string format: date-time - type: 'null' title: Closed At reopened_at: anyOf: - type: string format: date-time - type: 'null' title: Reopened At reopen_count: type: integer title: Reopen Count default: 0 reopen_reason: anyOf: - type: string - type: 'null' title: Reopen Reason created_at: type: string format: date-time title: Created At updated_at: type: string format: date-time title: Updated At created_by: anyOf: - type: string - type: 'null' title: Created By tags: additionalProperties: true type: object title: Tags sla_due_at: anyOf: - type: string format: date-time - type: 'null' title: Sla Due At fanout_results: anyOf: - items: $ref: '#/components/schemas/FanoutResult' type: array - type: 'null' title: Fanout Results type: object required: - id - title - description - severity - status - assignee - mitre_techniques - alert_ids - observable_graph - evidence_chain - compliance_frameworks - opened_at - triaged_at - resolved_at - closed_at - created_at - updated_at - created_by - tags - sla_due_at title: CaseResponse ChannelDecisionRequest: properties: decision: type: string title: Decision actor: type: string maxLength: 200 title: Actor default: '' note: type: string maxLength: 1000 title: Note default: '' type: object required: - decision title: ChannelDecisionRequest ChildTenantOut: properties: id: type: string format: uuid title: Id name: type: string title: Name mssp_role: type: string title: Mssp Role created_at: type: string title: Created At type: object required: - id - name - mssp_role - created_at title: ChildTenantOut CloseRequest: properties: analyst_note: anyOf: - type: string - type: 'null' title: Analyst Note type: object title: CloseRequest description: Optional analyst note appended to the auto-generated summary. ClosedSummary: properties: run_id: type: string format: uuid title: Run Id status: type: string title: Status artifact_id: type: string format: uuid title: Artifact Id summary_markdown: type: string title: Summary Markdown type: object required: - run_id - status - artifact_id - summary_markdown title: ClosedSummary description: Response body for POST /close. CollectEvidenceRequest: properties: framework: type: string title: Framework description: Compliance framework key e.g. 'SOC2'. control_id: type: string title: Control Id description: Control identifier e.g. 'CC7.2'. control_title: anyOf: - type: string - type: 'null' title: Control Title evidence_kind: type: string enum: - alert - log - screenshot - attestation - policy - runbook - other title: Evidence Kind default: alert summary: type: string minLength: 5 title: Summary raw_payload: additionalProperties: true type: object title: Raw Payload case_id: anyOf: - type: string format: uuid - type: 'null' title: Case Id type: object required: - framework - control_id - summary title: CollectEvidenceRequest CollectJobRequest: properties: framework: type: string title: Framework description: Compliance framework key e.g. 'SOC2'. scope: type: string title: Scope description: 'Collection scope: ''full'' or ''delta''.' default: full type: object required: - framework title: CollectJobRequest CollectJobResponse: properties: job_id: type: string format: uuid title: Job Id framework: type: string title: Framework scope: type: string title: Scope status: type: string title: Status queued_at: type: string format: date-time title: Queued At type: object required: - job_id - framework - scope - status - queued_at title: CollectJobResponse CollectResponse: properties: framework: type: string title: Framework collected: type: integer title: Collected manual: type: integer title: Manual results: items: $ref: '#/components/schemas/CollectedControl' type: array title: Results collected_at: type: string format: date-time title: Collected At type: object required: - framework - collected - manual - results - collected_at title: CollectResponse CollectedControl: properties: control_id: type: string title: Control Id outcome: type: string title: Outcome description: collected | manual detail: type: string title: Detail type: object required: - control_id - outcome - detail title: CollectedControl CollectionCreate: properties: title: type: string maxLength: 200 minLength: 1 title: Title description: type: string title: Description default: '' alias: anyOf: - type: string maxLength: 100 - type: 'null' title: Alias ioc_filter: additionalProperties: true type: object title: Ioc Filter is_enabled: type: boolean title: Is Enabled default: true type: object required: - title title: CollectionCreate CollectionOut: properties: id: type: string format: uuid title: Id title: type: string title: Title description: type: string title: Description alias: anyOf: - type: string - type: 'null' title: Alias can_read: type: boolean title: Can Read can_write: type: boolean title: Can Write ioc_filter: additionalProperties: true type: object title: Ioc Filter is_enabled: type: boolean title: Is Enabled created_at: type: string format: date-time title: Created At type: object required: - id - title - description - alias - can_read - can_write - ioc_filter - is_enabled - created_at title: CollectionOut CollectionResponse: properties: users: type: integer title: Users projects: type: integer title: Projects events: type: integer title: Events contentLabels: type: integer title: Contentlabels assets: type: integer title: Assets errors: items: type: string type: array title: Errors skipped: additionalProperties: type: string type: object title: Skipped type: object required: - users - projects - events - contentLabels - assets title: CollectionResponse description: 'What one collection pass did, per sink. Per sink rather than one total: a single number presented as the operation''s result reads as total failure when one sink failed and the others worked, which is how a threat-intel pass reported `indexed: 0` beside a successful write of 1,739 rows.' CommentResponse: properties: id: type: string format: uuid title: Id case_id: type: string format: uuid title: Case Id author: anyOf: - type: string - type: 'null' title: Author body: type: string title: Body is_system: type: boolean title: Is System created_at: type: string format: date-time title: Created At type: object required: - id - case_id - author - body - is_system - created_at title: CommentResponse CommunityPluginListOut: properties: total: type: integer title: Total items: items: $ref: '#/components/schemas/CommunityPluginOut' type: array title: Items type: object required: - total - items title: CommunityPluginListOut CommunityPluginOut: properties: id: type: string title: Id name: type: string title: Name version: type: string title: Version plugin_type: type: string title: Plugin Type description: type: string title: Description author: type: string title: Author tags: items: type: string type: array title: Tags default: [] status: $ref: '#/components/schemas/PublishStatus' default: pending install_count: type: integer title: Install Count default: 0 rating: type: number title: Rating default: 0.0 rating_count: type: integer title: Rating Count default: 0 verified: type: boolean title: Verified default: false submitted_at: type: string title: Submitted At approved_at: anyOf: - type: string - type: 'null' title: Approved At type: object required: - id - name - version - plugin_type - description - author - submitted_at title: CommunityPluginOut CompliancePosture: properties: framework: type: string title: Framework total_evidence: type: integer title: Total Evidence accepted: type: integer title: Accepted pending: type: integer title: Pending rejected: type: integer title: Rejected coverage_pct: type: number title: Coverage Pct controls_covered: items: type: string type: array title: Controls Covered controls_missing: items: type: string type: array title: Controls Missing generated_at: type: string format: date-time title: Generated At type: object required: - framework - total_evidence - accepted - pending - rejected - coverage_pct - controls_covered - controls_missing - generated_at title: CompliancePosture ConditionIn: properties: permission: type: string maxLength: 200 minLength: 3 title: Permission operator: type: string title: Operator value: title: Value role: anyOf: - type: string - type: 'null' title: Role description: anyOf: - type: string maxLength: 500 - type: 'null' title: Description enabled: type: boolean title: Enabled default: true type: object required: - permission - operator - value title: ConditionIn ConditionOut: properties: id: type: string format: uuid title: Id permission: type: string title: Permission role: anyOf: - type: string - type: 'null' title: Role operator: type: string title: Operator value: title: Value description: anyOf: - type: string - type: 'null' title: Description enabled: type: boolean title: Enabled created_at: type: string format: date-time title: Created At type: object required: - id - permission - role - operator - value - description - enabled - created_at title: ConditionOut ConfidenceBucket: properties: label: type: string title: Label floor: type: integer title: Floor ceil: type: integer title: Ceil count: type: integer title: Count activeCount: type: integer title: Activecount type: object required: - label - floor - ceil - count - activeCount title: ConfidenceBucket description: 'One column in the confidence histogram. ``label`` is the human-friendly bucket label rendered on the x-axis ("0–25", "26–50", …). ``floor`` / ``ceil`` are inclusive bounds so the UI can highlight the bucket a particular rule falls into without re-parsing the label.' ConfidenceResponse: properties: summary: $ref: '#/components/schemas/ConfidenceSummary' buckets: items: $ref: '#/components/schemas/ConfidenceBucket' type: array title: Buckets tactics: items: $ref: '#/components/schemas/TacticConfidence' type: array title: Tactics lowest: items: $ref: '#/components/schemas/ConfidenceRuleEntry' type: array title: Lowest highest: items: $ref: '#/components/schemas/ConfidenceRuleEntry' type: array title: Highest generatedAt: type: string title: Generatedat type: object required: - summary - buckets - tactics - lowest - highest - generatedAt title: ConfidenceResponse ConfidenceRuleEntry: properties: ruleId: type: string title: Ruleid name: type: string title: Name severity: type: string title: Severity enabled: type: boolean title: Enabled confidence: type: integer title: Confidence fpRate: type: number title: Fprate primaryTactic: anyOf: - type: string - type: 'null' title: Primarytactic type: object required: - ruleId - name - severity - enabled - confidence - fpRate title: ConfidenceRuleEntry description: A rule highlighted in the worst/best lists. ConfidenceSummary: properties: totalRules: type: integer title: Totalrules activeRules: type: integer title: Activerules avgConfidence: type: number title: Avgconfidence avgConfidenceActive: type: number title: Avgconfidenceactive medianConfidence: type: integer title: Medianconfidence lowConfidence: type: integer title: Lowconfidence unscored: type: integer title: Unscored default: 0 type: object required: - totalRules - activeRules - avgConfidence - avgConfidenceActive - medianConfidence - lowConfidence title: ConfidenceSummary ConfigRequest: properties: provider: type: string title: Provider description: 'One of: anthropic' accessKey: type: string minLength: 8 title: Accesskey description: Stored vault-encrypted; never returned. enabled: type: boolean title: Enabled description: Collect the directory and the activity feed. default: false contentLabels: type: boolean title: Contentlabels description: Additionally derive classification labels and one bounded snippet from message text. Off by default even when collection is on. default: false type: object required: - provider - accessKey title: ConfigRequest ConfigResponse: properties: configured: type: boolean title: Configured provider: anyOf: - type: string - type: 'null' title: Provider enabled: type: boolean title: Enabled default: false contentLabels: type: boolean title: Contentlabels default: false hasKey: type: boolean title: Haskey default: false lastCollectedAt: anyOf: - type: string - type: 'null' title: Lastcollectedat lastError: anyOf: - type: string - type: 'null' title: Lasterror type: object required: - configured title: ConfigResponse description: The tenant's configuration, without the key. ConfirmRequest: properties: mfa_token: anyOf: - type: string - type: 'null' title: Mfa Token code: type: string maxLength: 64 minLength: 1 title: Code type: object required: - code title: ConfirmRequest ConnectorHealthOut: properties: total: type: integer title: Total healthy: type: integer title: Healthy stale: type: integer title: Stale error: type: integer title: Error type: object required: - total - healthy - stale - error title: ConnectorHealthOut ConnectorHealthSummary: properties: total: type: integer title: Total healthy: type: integer title: Healthy unhealthy: type: integer title: Unhealthy unknown: type: integer title: Unknown drifted: type: integer title: Drifted drifted_recently: type: integer title: Drifted Recently last_drift_at: anyOf: - type: string format: date-time - type: 'null' title: Last Drift At total_events_ingested: type: integer title: Total Events Ingested total_events_dropped: type: integer title: Total Events Dropped type: object required: - total - healthy - unhealthy - unknown - drifted - drifted_recently - last_drift_at - total_events_ingested - total_events_dropped title: ConnectorHealthSummary description: 'Tenant-wide rollup of connector health for the dashboard. The web console shows a small "Connector Health" tile at the top of the Connectors page; this endpoint feeds that tile so the UI can render it without iterating the full list client-side.' ConnectorResponse: properties: id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id name: type: string title: Name connector_type: type: string title: Connector Type category: type: string title: Category is_enabled: type: boolean title: Is Enabled connector_config: additionalProperties: true type: object title: Connector Config health_status: type: string title: Health Status last_health_check: anyOf: - type: string format: date-time - type: 'null' title: Last Health Check last_sync: anyOf: - type: string format: date-time - type: 'null' title: Last Sync events_ingested: type: integer title: Events Ingested events_dropped: type: integer title: Events Dropped default: 0 error_count: type: integer title: Error Count schema_fingerprint: anyOf: - type: string - type: 'null' title: Schema Fingerprint last_schema_drift_at: anyOf: - type: string format: date-time - type: 'null' title: Last Schema Drift At last_drift_details: anyOf: - additionalProperties: true type: object - type: 'null' title: Last Drift Details last_event_at: anyOf: - type: string format: date-time - type: 'null' title: Last Event At last_event_kind: anyOf: - type: string - type: 'null' title: Last Event Kind oauth_provisioned: type: boolean title: Oauth Provisioned default: false allowed_capabilities: anyOf: - items: type: string type: array - type: 'null' title: Allowed Capabilities freshness: anyOf: - $ref: '#/components/schemas/FreshnessSLOResponse' - type: 'null' tags: items: {} type: array title: Tags created_at: type: string format: date-time title: Created At updated_at: type: string format: date-time title: Updated At type: object required: - id - tenant_id - name - connector_type - category - is_enabled - connector_config - health_status - last_health_check - last_sync - events_ingested - error_count - tags - created_at - updated_at title: ConnectorResponse description: 'Public projection of a ``Connector`` row. ``auth_config`` is intentionally **omitted**. Even decrypted credentials should never round-trip back to the wizard — the user typed them once and they belong inside the vault from then on. Wizard-driven re-edit flows reset specific secret fields rather than reading the existing ones. ``connector_config`` *is* included because it holds non-secret runtime knobs (poll interval, region, log filters) that the wizard surfaces so an operator can tweak them without re-pasting credentials.' ConsentRequest: properties: provider: type: string title: Provider uploads_enabled: type: boolean title: Uploads Enabled consent_text_version: anyOf: - type: string - type: 'null' title: Consent Text Version type: object required: - provider - uploads_enabled title: ConsentRequest ConsentResponse: properties: provider: type: string title: Provider uploads_enabled: type: boolean title: Uploads Enabled consent_text_version: anyOf: - type: string - type: 'null' title: Consent Text Version consent_text: type: string title: Consent Text type: object required: - provider - uploads_enabled - consent_text title: ConsentResponse ContentLabelListResponse: properties: items: items: $ref: '#/components/schemas/ContentLabelModel' type: array title: Items total: type: integer title: Total contentLabelsEnabled: type: boolean title: Contentlabelsenabled type: object required: - items - total - contentLabelsEnabled title: ContentLabelListResponse ContentLabelModel: properties: id: type: string title: Id chatExternalId: type: string title: Chatexternalid actorEmail: anyOf: - type: string - type: 'null' title: Actoremail labels: items: type: string type: array title: Labels secretKinds: items: type: string type: array title: Secretkinds snippet: anyOf: - type: string - type: 'null' title: Snippet snippetTruncated: type: boolean title: Snippettruncated default: false messagesExamined: type: integer title: Messagesexamined messagesMatched: type: integer title: Messagesmatched occurredAt: type: string title: Occurredat type: object required: - id - chatExternalId - messagesExamined - messagesMatched - occurredAt title: ContentLabelModel ContextImportRequest: properties: departments: items: additionalProperties: true type: object type: array title: Departments employees: items: additionalProperties: true type: object type: array title: Employees applications: items: additionalProperties: true type: object type: array title: Applications cloud_accounts: items: additionalProperties: true type: object type: array title: Cloud Accounts type: object title: ContextImportRequest description: 'Directory, HR and CMDB context the event stream cannot carry. An event can say an account authenticated. It cannot say which person holds that account, whether they still work here, which business application the host serves, or what an outage of it costs. That is the difference between "unusual login for svc_deploy" and "unusual login for svc_deploy, owned by a contractor whose last day was Friday, on the host running the tier-1 payments service".' ContextStatementModel: properties: statement: type: string title: Statement reason_code: type: string title: Reason Code scope: type: string title: Scope scope_value: type: string title: Scope Value observations: type: integer title: Observations expires_at: anyOf: - type: string - type: 'null' title: Expires At type: object required: - statement - reason_code - scope - scope_value - observations title: ContextStatementModel ContextStatementsResponse: properties: tenant_id: type: string title: Tenant Id statements: items: $ref: '#/components/schemas/ContextStatementModel' type: array title: Statements type: object required: - tenant_id - statements title: ContextStatementsResponse Control: properties: id: type: string title: Id framework: type: string title: Framework control_id: type: string title: Control Id category: type: string title: Category title: type: string title: Title description: anyOf: - type: string - type: 'null' title: Description automatable: type: boolean title: Automatable default: false type: object required: - id - framework - control_id - category - title title: Control ControlItem: properties: control_id: type: string title: Control Id title: type: string title: Title type: object required: - control_id - title title: ControlItem ControlWithEvidence: properties: control: $ref: '#/components/schemas/Control' evidence: items: $ref: '#/components/schemas/EvidenceItem' type: array title: Evidence latest_status: type: string title: Latest Status description: accepted | pending | rejected | missing type: object required: - control - evidence - latest_status title: ControlWithEvidence ControlsResponse: properties: framework_id: type: string title: Framework Id controls: items: $ref: '#/components/schemas/ControlItem' type: array title: Controls type: object required: - framework_id - controls title: ControlsResponse CostAggregateResponse: properties: window_days: type: integer title: Window Days by_model: items: $ref: '#/components/schemas/CostAggregateRow' type: array title: By Model totals: anyOf: - $ref: '#/components/schemas/CostAggregateRow' - type: 'null' type: object required: - window_days - by_model - totals title: CostAggregateResponse CostAggregateRow: properties: model: type: string title: Model runs: type: integer title: Runs calls: type: integer title: Calls total_prompt_tokens: type: integer title: Total Prompt Tokens total_completion_tokens: type: integer title: Total Completion Tokens total_cost_usd: type: number title: Total Cost Usd measured_call_count: type: integer title: Measured Call Count default: 0 estimated_cost_usd: type: number title: Estimated Cost Usd default: 0.0 estimated_call_count: type: integer title: Estimated Call Count default: 0 unpriced_call_count: type: integer title: Unpriced Call Count default: 0 total_latency_ms: type: integer title: Total Latency Ms avg_cost_per_run: type: number title: Avg Cost Per Run avg_latency_per_call_ms: type: number title: Avg Latency Per Call Ms type: object required: - model - runs - calls - total_prompt_tokens - total_completion_tokens - total_cost_usd - total_latency_ms - avg_cost_per_run - avg_latency_per_call_ms title: CostAggregateRow description: Aggregate spend grouped by model across runs. CostBucket: properties: day: type: string format: date title: Day total_cost_usd: type: number title: Total Cost Usd measured_call_count: type: integer title: Measured Call Count default: 0 estimated_cost_usd: type: number title: Estimated Cost Usd default: 0.0 estimated_call_count: type: integer title: Estimated Call Count default: 0 unpriced_call_count: type: integer title: Unpriced Call Count default: 0 total_tokens: type: integer title: Total Tokens call_count: type: integer title: Call Count type: object required: - day - total_cost_usd - total_tokens - call_count title: CostBucket description: 'LLM spend bucketed by day. ``total_cost_usd`` is measured cost. ``measured_call_count`` says how much of the day''s traffic that covers, so a bar of height zero can be read as "a free day" or "an unmeasured day" rather than silently as the first.' CostDashboard: properties: tenant_id: type: string format: uuid title: Tenant Id period: $ref: '#/components/schemas/DashboardPeriod' headline: $ref: '#/components/schemas/CostHeadline' daily_costs: items: $ref: '#/components/schemas/CostBucket' type: array title: Daily Costs by_model: items: $ref: '#/components/schemas/ModelBreakdown' type: array title: By Model top_cases: items: $ref: '#/components/schemas/TopCostCase' type: array title: Top Cases action_counts: items: $ref: '#/components/schemas/ActionCount' type: array title: Action Counts byok_savings: $ref: '#/components/schemas/ByokSavings' storage: $ref: '#/components/schemas/StorageCostProjection' type: object required: - tenant_id - period - headline - byok_savings - storage title: CostDashboard description: Top-level deterministic snapshot. CostHeadline: properties: total_cost_usd: type: number title: Total Cost Usd measured_call_count: type: integer title: Measured Call Count default: 0 estimated_cost_usd: type: number title: Estimated Cost Usd default: 0.0 estimated_call_count: type: integer title: Estimated Call Count default: 0 unpriced_call_count: type: integer title: Unpriced Call Count default: 0 total_tokens: type: integer title: Total Tokens total_calls: type: integer title: Total Calls total_runs: type: integer title: Total Runs avg_cost_per_run_usd: anyOf: - type: number - type: 'null' title: Avg Cost Per Run Usd type: object required: - total_cost_usd - total_tokens - total_calls - total_runs - avg_cost_per_run_usd title: CostHeadline description: 'Window totals, each with the count of calls it was computed over. ``total_cost_usd`` is **measured** spend. When ``measured_call_count`` is zero it is not a total of anything and the console must render "not measured" — the same contract the MTTR tiles use.' CoverageCell: properties: techniqueId: type: string title: Techniqueid tactic: anyOf: - type: string - type: 'null' title: Tactic techniqueName: anyOf: - type: string - type: 'null' title: Techniquename totalRules: type: integer title: Totalrules activeRules: type: integer title: Activerules inactiveRules: type: integer title: Inactiverules type: object required: - techniqueId - totalRules - activeRules - inactiveRules title: CoverageCell description: 'One technique cell in the rule-centric MITRE coverage heatmap. ``intensity`` is a 0-1 ratio that the heatmap uses to pick a color bucket; it''s max(activeRules, 1) / max-active-in-grid normalized client-side so we don''t have to know the global max here.' CoverageResponse: properties: tactics: items: type: string type: array title: Tactics cells: items: $ref: '#/components/schemas/CoverageCell' type: array title: Cells summary: $ref: '#/components/schemas/CoverageSummary' generatedAt: type: string title: Generatedat type: object required: - tactics - cells - summary - generatedAt title: CoverageResponse CoverageSummary: properties: totalRules: type: integer title: Totalrules activeRules: type: integer title: Activerules inactiveRules: type: integer title: Inactiverules techniques: type: integer title: Techniques coveredTechniques: type: integer title: Coveredtechniques type: object required: - totalRules - activeRules - inactiveRules - techniques - coveredTechniques title: CoverageSummary CreateApiKeyRequest: properties: name: type: string maxLength: 255 minLength: 1 title: Name description: Human-readable name scopes: items: type: string type: array title: Scopes description: Permission scopes granted to this key expires_in_days: anyOf: - type: integer maximum: 3650.0 minimum: 1.0 - type: 'null' title: Expires In Days description: TTL in days; None = never expires type: object required: - name title: CreateApiKeyRequest CreateApiKeyResponse: properties: id: type: string format: uuid title: Id name: type: string title: Name key: type: string title: Key description: Raw API key — shown ONCE, store securely prefix: type: string title: Prefix scopes: items: type: string type: array title: Scopes expires_at: anyOf: - type: string format: date-time - type: 'null' title: Expires At created_at: type: string format: date-time title: Created At type: object required: - id - name - key - prefix - scopes - expires_at - created_at title: CreateApiKeyResponse CreateBaselineRequest: properties: suite: type: string maxLength: 64 title: Suite score: type: number title: Score payload: additionalProperties: true type: object title: Payload type: object required: - suite - score title: CreateBaselineRequest CreateBody: properties: name: type: string title: Name description: anyOf: - type: string - type: 'null' title: Description language: type: string title: Language body: type: string title: Body enabled: type: boolean title: Enabled default: true tags: items: type: string type: array title: Tags mitre: items: type: string type: array title: Mitre severity: type: string title: Severity default: medium category: type: string title: Category default: custom type: object required: - name - language - body title: CreateBody CreateCaseRequest: properties: title: type: string minLength: 3 title: Title description: anyOf: - type: string - type: 'null' title: Description severity: type: string enum: - info - low - medium - high - critical title: Severity default: medium assignee: anyOf: - type: string - type: 'null' title: Assignee alert_ids: items: type: string format: uuid type: array title: Alert Ids mitre_techniques: items: type: string type: array title: Mitre Techniques compliance_frameworks: items: type: string type: array title: Compliance Frameworks tags: additionalProperties: type: string type: object title: Tags sla_due_at: anyOf: - type: string format: date-time - type: 'null' title: Sla Due At push_to_connector_ids: items: type: string format: uuid type: array title: Push To Connector Ids type: object required: - title title: CreateCaseRequest CreateConnectorRequest: properties: name: type: string maxLength: 255 minLength: 1 title: Name connector_type: type: string maxLength: 100 minLength: 1 title: Connector Type category: anyOf: - type: string maxLength: 50 - type: 'null' title: Category description: Optional override; defaults to the catalog entry's category. auth_config: additionalProperties: true type: object title: Auth Config connector_config: additionalProperties: true type: object title: Connector Config tags: items: type: string type: array title: Tags type: object required: - name - connector_type title: CreateConnectorRequest CreateHuntRequest: properties: title: type: string minLength: 3 title: Title hypothesis: type: string minLength: 10 title: Hypothesis description: Plain-English threat hypothesis. mitre_tactic: anyOf: - type: string - type: 'null' title: Mitre Tactic mitre_technique: anyOf: - type: string - type: 'null' title: Mitre Technique priority: type: string enum: - low - medium - high - critical title: Priority default: medium assigned_to: anyOf: - type: string - type: 'null' title: Assigned To tags: items: type: string type: array title: Tags type: object required: - title - hypothesis title: CreateHuntRequest CreateProposalRequest: properties: name: type: string maxLength: 255 title: Name description: anyOf: - type: string - type: 'null' title: Description rule_language: type: string maxLength: 30 title: Rule Language rule_body: type: string title: Rule Body category: type: string maxLength: 100 title: Category severity: type: string title: Severity default: medium confidence: type: integer maximum: 100.0 minimum: 0.0 title: Confidence default: 50 mitre_tactics: items: type: string type: array title: Mitre Tactics mitre_techniques: items: type: string type: array title: Mitre Techniques tags: items: type: string type: array title: Tags base_rule_id: anyOf: - type: string format: uuid - type: 'null' title: Base Rule Id positive_fixtures: items: additionalProperties: true type: object type: array maxItems: 200 title: Positive Fixtures negative_fixtures: items: additionalProperties: true type: object type: array maxItems: 200 title: Negative Fixtures type: object required: - name - rule_language - rule_body - category title: CreateProposalRequest CreateRuleRequest: properties: name: type: string title: Name description: anyOf: - type: string - type: 'null' title: Description rule_language: type: string title: Rule Language rule_body: type: string title: Rule Body category: type: string title: Category severity: type: string title: Severity default: medium confidence: type: integer title: Confidence default: 50 mitre_tactics: items: type: string type: array title: Mitre Tactics default: [] mitre_techniques: items: type: string type: array title: Mitre Techniques default: [] tags: items: type: string type: array title: Tags default: [] type: object required: - name - rule_language - rule_body - category title: CreateRuleRequest CreateSavedHuntRequest: properties: name: type: string maxLength: 160 minLength: 1 title: Name nl_query: type: string maxLength: 4000 minLength: 3 title: Nl Query language: type: string enum: - esql - kql - spl title: Language default: esql schedule: anyOf: - type: string maxLength: 120 - type: 'null' title: Schedule type: object required: - name - nl_query title: CreateSavedHuntRequest description: 'Body for ``POST /saved-hunts``. The caller supplies the NL question and a name; we translate server-side so the stored ``translated_query`` is guaranteed to match what the platform will execute (no client-side drift).' CreateSavedViewRequest: properties: view_type: type: string title: View Type name: type: string maxLength: 120 minLength: 1 title: Name filters: additionalProperties: true type: object title: Filters columns: anyOf: - items: {} type: array - additionalProperties: true type: object - type: 'null' title: Columns is_default: type: boolean title: Is Default default: false type: object required: - view_type - name title: CreateSavedViewRequest CreateScimTokenRequest: properties: name: type: string maxLength: 200 minLength: 1 title: Name description: Which identity provider this credential is for expires_in_days: anyOf: - type: integer maximum: 3650.0 minimum: 1.0 - type: 'null' title: Expires In Days description: Optional lifetime; omit for no expiry type: object required: - name title: CreateScimTokenRequest CreateTaskRequest: properties: title: type: string minLength: 1 title: Title status: type: string enum: - todo - in_progress - done title: Status default: todo assignee: anyOf: - type: string - type: 'null' title: Assignee dueAt: anyOf: - type: string format: date-time - type: 'null' title: Dueat type: object required: - title title: CreateTaskRequest CreateUserRequest: properties: email: type: string format: email title: Email username: type: string title: Username password: type: string title: Password role: type: string title: Role default: soc_analyst type: object required: - email - username - password title: CreateUserRequest CreatedScimTokenOut: properties: id: type: string format: uuid title: Id name: type: string title: Name prefix: type: string title: Prefix org_id: anyOf: - type: string format: uuid - type: 'null' title: Org Id 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 expires_at: anyOf: - type: string format: date-time - type: 'null' title: Expires At revoked_at: anyOf: - type: string format: date-time - type: 'null' title: Revoked At rotated_from_id: anyOf: - type: string format: uuid - type: 'null' title: Rotated From Id active: type: boolean title: Active token: type: string title: Token description: The raw credential. Shown once and not recoverable. type: object required: - id - name - prefix - org_id - created_at - last_used_at - expires_at - revoked_at - rotated_from_id - active - token title: CreatedScimTokenOut CredentialsListResponse: properties: items: items: $ref: '#/components/schemas/PasskeyCredentialOut' type: array title: Items type: object required: - items title: CredentialsListResponse CspmScanRequest: properties: resources: items: additionalProperties: true type: object type: array title: Resources description: Normalised cloud-resource snapshot. type: object title: CspmScanRequest CspmScanResponse: properties: findings: items: additionalProperties: true type: object type: array title: Findings summary: additionalProperties: type: integer type: object title: Summary evidence: items: additionalProperties: true type: object type: array title: Evidence type: object required: - findings - summary - evidence title: CspmScanResponse CustomFieldIn: properties: key: type: string maxLength: 64 minLength: 1 pattern: ^[a-z][a-z0-9_]*$ title: Key label: type: string maxLength: 120 minLength: 1 title: Label field_type: type: string title: Field Type help_text: type: string maxLength: 400 title: Help Text default: '' choices: items: type: string type: array maxItems: 50 title: Choices required: type: boolean title: Required default: false position: type: integer maximum: 999.0 minimum: 0.0 title: Position default: 0 type: object required: - key - label - field_type title: CustomFieldIn CustomFieldOut: properties: id: type: string title: Id key: type: string title: Key label: type: string title: Label help_text: type: string title: Help Text field_type: type: string title: Field Type choices: items: type: string type: array title: Choices required: type: boolean title: Required position: type: integer title: Position active: type: boolean title: Active type: object required: - id - key - label - help_text - field_type - choices - required - position - active title: CustomFieldOut CustomFieldPatch: properties: label: anyOf: - type: string maxLength: 120 - type: 'null' title: Label help_text: anyOf: - type: string maxLength: 400 - type: 'null' title: Help Text choices: anyOf: - items: type: string type: array maxItems: 50 - type: 'null' title: Choices required: anyOf: - type: boolean - type: 'null' title: Required position: anyOf: - type: integer maximum: 999.0 minimum: 0.0 - type: 'null' title: Position active: anyOf: - type: boolean - type: 'null' title: Active type: object title: CustomFieldPatch CustomValueOut: properties: key: type: string title: Key label: type: string title: Label field_type: type: string title: Field Type value: type: string title: Value required: type: boolean title: Required updated_by: type: string title: Updated By updated_at: anyOf: - type: string format: date-time - type: 'null' title: Updated At type: object required: - key - label - field_type - value - required - updated_by - updated_at title: CustomValueOut CustomValuesIn: properties: values: additionalProperties: type: string type: object title: Values type: object required: - values title: CustomValuesIn DashboardMetrics: properties: period: type: string title: Period default: 24h alerts: $ref: '#/components/schemas/AlertMetrics' cases: $ref: '#/components/schemas/CaseMetrics' sources: items: $ref: '#/components/schemas/SourceStat' type: array title: Sources topMitre: items: $ref: '#/components/schemas/MitreTactic' type: array title: Topmitre alertsTrend: items: $ref: '#/components/schemas/TrendPoint' type: array title: Alertstrend threatsBySource: items: $ref: '#/components/schemas/SourceThreat' type: array title: Threatsbysource type: object required: - alerts - cases - sources - topMitre - alertsTrend - threatsBySource title: DashboardMetrics DashboardPeriod: properties: start: type: string format: date-time title: Start end: type: string format: date-time title: End window_days: type: integer title: Window Days label: type: string title: Label type: object required: - start - end - window_days - label title: DashboardPeriod DecideRequest: properties: decision: type: string title: Decision type: object required: - decision title: DecideRequest DecisionRequest: properties: decision: type: string enum: - approve - reject title: Decision comment: anyOf: - type: string maxLength: 2000 - type: 'null' title: Comment type: object required: - decision title: DecisionRequest DelegationCreate: properties: child_tenant_id: type: string format: uuid title: Child Tenant Id granted_role: type: string title: Granted Role default: soc_analyst expires_at: anyOf: - type: string format: date-time - type: 'null' title: Expires At type: object required: - child_tenant_id title: DelegationCreate DelegationOut: properties: child_tenant_id: type: string format: uuid title: Child Tenant Id granted_role: type: string title: Granted Role default: soc_analyst expires_at: anyOf: - type: string format: date-time - type: 'null' title: Expires At id: type: string format: uuid title: Id parent_tenant_id: type: string format: uuid title: Parent Tenant Id granted_by_user: anyOf: - type: string format: uuid - type: 'null' title: Granted By User revoked_at: anyOf: - type: string format: date-time - type: 'null' title: Revoked At created_at: type: string title: Created At type: object required: - child_tenant_id - id - parent_tenant_id - granted_by_user - revoked_at - created_at title: DelegationOut DeliveryOut: properties: id: type: string format: uuid title: Id webhook_id: type: string format: uuid title: Webhook Id event_type: type: string title: Event Type status: type: string title: Status attempts: type: integer title: Attempts last_status_code: anyOf: - type: integer - type: 'null' title: Last Status Code last_error: type: string title: Last Error created_at: type: string format: date-time title: Created At delivered_at: anyOf: - type: string format: date-time - type: 'null' title: Delivered At type: object required: - id - webhook_id - event_type - status - attempts - last_status_code - last_error - created_at - delivered_at title: DeliveryOut DeploymentConfig: properties: mode: $ref: '#/components/schemas/DeploymentMode' llm_provider: $ref: '#/components/schemas/LLMProvider' update_channel: $ref: '#/components/schemas/UpdateChannel' telemetry_enabled: type: boolean title: Telemetry Enabled auto_update: type: boolean title: Auto Update last_updated: type: string title: Last Updated type: object required: - mode - llm_provider - update_channel - telemetry_enabled - auto_update - last_updated title: DeploymentConfig DeploymentConfigUpdate: properties: mode: anyOf: - $ref: '#/components/schemas/DeploymentMode' - type: 'null' llm_provider: anyOf: - $ref: '#/components/schemas/LLMProvider' - type: 'null' update_channel: anyOf: - $ref: '#/components/schemas/UpdateChannel' - type: 'null' telemetry_enabled: anyOf: - type: boolean - type: 'null' title: Telemetry Enabled auto_update: anyOf: - type: boolean - type: 'null' title: Auto Update type: object title: DeploymentConfigUpdate DeploymentMode: type: string enum: - cloud - hybrid - airgap title: DeploymentMode DestinationPreviewRequest: properties: kind: type: string title: Kind description: opsgenie | email | webhook alert: additionalProperties: true type: object title: Alert recipients: items: type: string type: array title: Recipients type: object required: - kind - alert title: DestinationPreviewRequest DetectionRuleResponse: properties: id: type: string format: uuid title: Id tenant_id: anyOf: - type: string format: uuid - type: 'null' title: Tenant Id name: type: string title: Name description: anyOf: - type: string - type: 'null' title: Description rule_language: type: string title: Rule Language rule_body: type: string title: Rule Body category: type: string title: Category status: type: string title: Status severity: type: string title: Severity confidence: type: integer title: Confidence mitre_tactics: items: {} type: array title: Mitre Tactics mitre_techniques: items: {} type: array title: Mitre Techniques fp_rate: type: number title: Fp Rate total_hits: type: integer title: Total Hits last_triggered: anyOf: - type: string format: date-time - type: 'null' title: Last Triggered tags: items: {} type: array title: Tags is_builtin: type: boolean title: Is Builtin version: type: integer title: Version created_at: type: string format: date-time title: Created At updated_at: type: string format: date-time title: Updated At source_id: anyOf: - type: string - type: 'null' title: Source Id confidence_measured: type: boolean title: Confidence Measured default: true tuned: type: boolean title: Tuned default: true engine: type: string title: Engine default: custom type: object required: - id - tenant_id - name - description - rule_language - rule_body - category - status - severity - confidence - mitre_tactics - mitre_techniques - fp_rate - total_hits - last_triggered - tags - is_builtin - version - created_at - updated_at title: DetectionRuleResponse description: "A rule in the tenant's effective library.\n\nFour fields exist\ \ to keep a compiled built-in legible without inventing\ndata the compiled\ \ corpus does not hold:\n\n``source_id``\n The engine's own rule id — ``det-cloud-063``,\n\ \ ``sigmahq-sigma-002bdb95-…`` — which is what an alert carries and\n \ \ therefore what an analyst searches for. ``None`` for a tenant's own\n\ \ rule, which the engine does not load.\n``confidence_measured``\n ``False``\ \ when ``confidence`` is a placeholder rather than a\n measurement. The\ \ compiled artefacts record a rule's logic, severity\n and ATT&CK mapping\ \ and nothing operational, so an untuned built-in\n has no confidence,\ \ no FP rate and no trigger history. The two panels\n that average those\ \ columns exclude such rules and publish the count.\n``tuned``\n Whether\ \ this tenant has stored any decision about the rule.\n``engine``\n ``aisoc-engine``\ \ when fusion loads and evaluates this rule,\n ``custom`` when it does\ \ not. Disabling the two means different\n things and a reader has to be\ \ able to tell them apart." DisableRequest: properties: code: type: string maxLength: 64 minLength: 1 title: Code type: object required: - code title: DisableRequest DiscoverResponse: properties: discovered: items: type: string type: array title: Discovered type: object required: - discovered title: DiscoverResponse DiscoveredModelView: properties: id: type: string title: Id name: anyOf: - type: string - type: 'null' title: Name type: object required: - id title: DiscoveredModelView DismissTuningRequest: properties: reason: anyOf: - type: string maxLength: 255 - type: 'null' title: Reason type: object title: DismissTuningRequest description: Body for ``POST /detection/tuning/{rule_id}/dismiss``. DlqReplayRequest: properties: topic: type: string maxLength: 200 minLength: 1 title: Topic partition: type: integer minimum: 0.0 title: Partition start_offset: type: integer minimum: 0.0 title: Start Offset max_messages: type: integer maximum: 1000.0 minimum: 1.0 title: Max Messages default: 100 execute: type: boolean title: Execute default: false type: object required: - topic - partition - start_offset title: DlqReplayRequest description: An explicit, bounded range of refused messages to re-check. DlqReplayResponse: properties: replay_id: type: string format: uuid title: Replay Id status: type: string title: Status executed: type: boolean title: Executed messages_read: type: integer title: Messages Read default: 0 would_pass: type: integer title: Would Pass default: 0 refused: type: integer title: Refused default: 0 produced: type: integer title: Produced default: 0 refusals: additionalProperties: type: integer type: object title: Refusals produced_offsets: items: type: integer type: array title: Produced Offsets error: anyOf: - type: string - type: 'null' title: Error type: object required: - replay_id - status - executed title: DlqReplayResponse description: What the replay found, and what it did about it. DriftEntry: properties: ruleId: type: string title: Ruleid name: type: string title: Name severity: type: string title: Severity enabled: type: boolean title: Enabled confidence: type: integer title: Confidence fpRate: type: number title: Fprate lastTriggeredAt: anyOf: - type: string - type: 'null' title: Lasttriggeredat daysSinceTriggered: anyOf: - type: integer - type: 'null' title: Dayssincetriggered issues: items: type: string type: array title: Issues type: object required: - ruleId - name - severity - enabled - confidence - fpRate - issues title: DriftEntry description: 'One rule that has drifted from its tuning baseline. ``issues`` enumerates *which* heuristics flagged it; the UI renders one chip per issue so analysts see the reason without having to read the metric values.' DriftResponse: properties: entries: items: $ref: '#/components/schemas/DriftEntry' type: array title: Entries summary: $ref: '#/components/schemas/DriftSummary' generatedAt: type: string title: Generatedat type: object required: - entries - summary - generatedAt title: DriftResponse DriftSummary: properties: total: type: integer title: Total highFpRate: type: integer title: Highfprate lowConfidence: type: integer title: Lowconfidence stale: type: integer title: Stale unscored: type: integer title: Unscored default: 0 type: object required: - total - highFpRate - lowConfidence - stale title: DriftSummary EdgeCreate: properties: source_id: type: string format: uuid title: Source Id target_id: type: string format: uuid title: Target Id edge_type: type: string title: Edge Type weight: type: number title: Weight default: 1.0 valid_until: anyOf: - type: string format: date-time - type: 'null' title: Valid Until evidence: additionalProperties: true type: object title: Evidence type: object required: - source_id - target_id - edge_type title: EdgeCreate EdgeOut: properties: source_id: type: string format: uuid title: Source Id target_id: type: string format: uuid title: Target Id edge_type: type: string title: Edge Type weight: type: number title: Weight default: 1.0 valid_until: anyOf: - type: string format: date-time - type: 'null' title: Valid Until evidence: additionalProperties: true type: object title: Evidence id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id valid_from: type: string title: Valid From created_at: type: string title: Created At type: object required: - source_id - target_id - edge_type - id - tenant_id - valid_from - created_at title: EdgeOut EffectiveAutonomy: properties: tier: type: string title: Tier tier_label: type: string title: Tier Label tier_source: type: string title: Tier Source max_automatic_impact: anyOf: - type: string - type: 'null' title: Max Automatic Impact auto_executing_actions: items: type: string type: array title: Auto Executing Actions high_blast_auto_executing: items: type: string type: array title: High Blast Auto Executing unimplemented_actions: items: type: string type: array title: Unimplemented Actions thresholds_are_advisory: type: boolean title: Thresholds Are Advisory advisory_note: type: string title: Advisory Note type: object required: - tier - tier_label - tier_source - max_automatic_impact - auto_executing_actions - high_blast_auto_executing - unimplemented_actions - thresholds_are_advisory - advisory_note title: EffectiveAutonomy description: The control that actually gates execution, as the console must show it. EffectiveRuleCountOut: properties: total: type: integer title: Total tenant: type: integer title: Tenant builtin: type: integer title: Builtin pack: type: integer title: Pack excluded: type: integer title: Excluded type: object required: - total - tenant - builtin - pack - excluded title: EffectiveRuleCountOut EffectiveRuleOut: properties: id: type: string format: uuid title: Id name: type: string title: Name rule_language: type: string title: Rule Language severity: type: string title: Severity category: anyOf: - type: string - type: 'null' title: Category status: type: string title: Status is_builtin: type: boolean title: Is Builtin source: type: string title: Source pack_ids: items: type: string format: uuid type: array title: Pack Ids severity_overridden: type: boolean title: Severity Overridden original_severity: anyOf: - type: string - type: 'null' title: Original Severity override_note: anyOf: - type: string - type: 'null' title: Override Note parameter_overrides: additionalProperties: true type: object title: Parameter Overrides type: object required: - id - name - rule_language - severity - category - status - is_builtin - source - pack_ids - severity_overridden - original_severity - override_note - parameter_overrides title: EffectiveRuleOut ElevationOut: properties: id: type: string format: uuid title: Id user_id: type: string format: uuid title: User Id user_email: anyOf: - type: string - type: 'null' title: User Email permissions: items: type: string type: array title: Permissions justification: type: string title: Justification approved_by_id: anyOf: - type: string format: uuid - type: 'null' title: Approved By Id granted_at: type: string format: date-time title: Granted At expires_at: type: string format: date-time title: Expires At revoked_at: anyOf: - type: string format: date-time - type: 'null' title: Revoked At case_id: anyOf: - type: string format: uuid - type: 'null' title: Case Id alert_id: anyOf: - type: string format: uuid - type: 'null' title: Alert Id state: type: string title: State type: object required: - id - user_id - permissions - justification - approved_by_id - granted_at - expires_at - revoked_at - case_id - alert_id - state title: ElevationOut ElevationRequest: properties: permissions: items: type: string type: array maxItems: 20 minItems: 1 title: Permissions justification: type: string maxLength: 2000 minLength: 10 title: Justification duration_minutes: type: integer maximum: 480.0 minimum: 5.0 title: Duration Minutes default: 60 case_id: anyOf: - type: string format: uuid - type: 'null' title: Case Id alert_id: anyOf: - type: string format: uuid - type: 'null' title: Alert Id type: object required: - permissions - justification title: ElevationRequest EnforcementPolicy: properties: enforce: items: type: string type: array title: Enforce type: object title: EnforcementPolicy description: 'Which limit keys this tenant refuses work on, rather than only reporting. The whole set is sent, not a delta, so clearing a key from the console actually clears it. A merge would leave the page able to add a restriction and unable to remove one.' EngageRequest: properties: reason: type: string maxLength: 500 minLength: 8 title: Reason description: 'Why. Required: the next operator has to be able to tell a deliberate freeze from a forgotten test.' type: object required: - reason title: EngageRequest EnrollBegin: properties: secret: type: string title: Secret otpauth_uri: type: string title: Otpauth Uri type: object required: - secret - otpauth_uri title: EnrollBegin EnrollConfirmed: properties: recovery_codes: items: type: string type: array title: Recovery Codes access_token: anyOf: - type: string - type: 'null' title: Access Token refresh_token: anyOf: - type: string - type: 'null' title: Refresh Token type: object required: - recovery_codes title: EnrollConfirmed EntityNeighborsResponse: properties: entity_id: type: string title: Entity Id entity_type: type: string title: Entity Type source: anyOf: - $ref: '#/components/schemas/GraphNode' - type: 'null' neighbors: items: additionalProperties: true type: object type: array title: Neighbors neighbor_count: type: integer title: Neighbor Count default: 0 type: object required: - entity_id - entity_type - source - neighbors title: EntityNeighborsResponse EvalAttachRequest: properties: eval_report: additionalProperties: true type: object title: Eval Report description: Full JSON output of `python3 scripts/run_evals.py --baseline ... --max-regression-pp ...` for the candidate ruleset. max_regression_pp: type: number maximum: 50.0 minimum: 0.0 title: Max Regression Pp description: Allowed MITRE accuracy regression vs baseline, in pp. Hard-capped at 50 — anything above that is indistinguishable from disabling the regression gate entirely. default: 1.0 type: object required: - eval_report title: EvalAttachRequest description: Attach a `run_evals.py` JSON report to a proposal. EvaluateRuleRequest: properties: positive_fixtures: items: additionalProperties: true type: object type: array maxItems: 200 title: Positive Fixtures description: 'Events the rule MUST fire on. Optional: when omitted, the fixtures the proposal was created with are replayed instead. The handler refuses if neither source yields a positive, because a rule that cannot be shown to catch anything must not be approved.' negative_fixtures: items: additionalProperties: true type: object type: array maxItems: 200 title: Negative Fixtures description: Benign near-miss events the rule MUST NOT fire on. type: object title: EvaluateRuleRequest description: 'Evaluate the candidate rule body against its own fixtures (Phase 4). This is the non-circular gate: unlike ``/run-eval`` (which measures the repo-wide substrate benchmark and is independent of the proposed rule), this runs the *proposed rule body itself* through the runtime engine and asserts it fires on every positive fixture and stays silent on every negative one. Approval now requires this verdict to pass.' EvaluationDetail: properties: id: type: string format: uuid title: Id status: type: string title: Status error: anyOf: - type: string - type: 'null' title: Error connector_id: type: string title: Connector Id vendor: type: string title: Vendor window_start: type: string format: date-time title: Window Start window_end: type: string format: date-time title: Window End train_fraction: type: number title: Train Fraction bootstrap_seed: type: integer title: Bootstrap Seed bootstrap_resamples: type: integer title: Bootstrap Resamples findings_read: type: integer title: Findings Read findings_labelled: type: integer title: Findings Labelled decisions_recorded: type: integer title: Decisions Recorded graded: type: integer title: Graded malicious_support: type: integer title: Malicious Support headline_accuracy: anyOf: - type: number - type: 'null' title: Headline Accuracy headline_withheld_reason: anyOf: - type: string - type: 'null' title: Headline Withheld Reason malicious_recall: anyOf: - type: number - type: 'null' title: Malicious Recall created_at: type: string format: date-time title: Created 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 is_terminal: type: boolean title: Is Terminal score: anyOf: - additionalProperties: true type: object - type: 'null' title: Score method: anyOf: - additionalProperties: true type: object - type: 'null' title: Method report_markdown: anyOf: - type: string - type: 'null' title: Report Markdown type: object required: - id - status - connector_id - vendor - window_start - window_end - train_fraction - bootstrap_seed - bootstrap_resamples - findings_read - findings_labelled - decisions_recorded - graded - malicious_support - created_at - is_terminal title: EvaluationDetail description: A completed run with its report and the provenance behind it. EvaluationSummary: properties: id: type: string format: uuid title: Id status: type: string title: Status error: anyOf: - type: string - type: 'null' title: Error connector_id: type: string title: Connector Id vendor: type: string title: Vendor window_start: type: string format: date-time title: Window Start window_end: type: string format: date-time title: Window End train_fraction: type: number title: Train Fraction bootstrap_seed: type: integer title: Bootstrap Seed bootstrap_resamples: type: integer title: Bootstrap Resamples findings_read: type: integer title: Findings Read findings_labelled: type: integer title: Findings Labelled decisions_recorded: type: integer title: Decisions Recorded graded: type: integer title: Graded malicious_support: type: integer title: Malicious Support headline_accuracy: anyOf: - type: number - type: 'null' title: Headline Accuracy headline_withheld_reason: anyOf: - type: string - type: 'null' title: Headline Withheld Reason malicious_recall: anyOf: - type: number - type: 'null' title: Malicious Recall created_at: type: string format: date-time title: Created 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 is_terminal: type: boolean title: Is Terminal type: object required: - id - status - connector_id - vendor - window_start - window_end - train_fraction - bootstrap_seed - bootstrap_resamples - findings_read - findings_labelled - decisions_recorded - graded - malicious_support - created_at - is_terminal title: EvaluationSummary description: One run as the list and the poll return it. EventListResponse: properties: items: items: $ref: '#/components/schemas/EventOut' type: array title: Items total: type: integer title: Total since: anyOf: - type: integer - type: 'null' title: Since next_seq: anyOf: - type: integer - type: 'null' title: Next Seq type: object required: - items - total - since - next_seq title: EventListResponse EventOut: properties: id: type: string format: uuid title: Id run_id: type: string format: uuid title: Run Id seq: type: integer title: Seq ts: type: string format: date-time title: Ts kind: type: string title: Kind agent: type: string title: Agent summary: type: string title: Summary payload: anyOf: - additionalProperties: true type: object - type: 'null' title: Payload input_hash: anyOf: - type: string - type: 'null' title: Input Hash output_hash: anyOf: - type: string - type: 'null' title: Output Hash duration_ms: type: integer title: Duration Ms type: object required: - id - run_id - seq - ts - kind - agent - summary - payload - input_hash - output_hash - duration_ms title: EventOut EvidenceItem: properties: id: type: string title: Id control_id: type: string title: Control Id evidence_type: type: string title: Evidence Type title: type: string title: Title description: anyOf: - type: string - type: 'null' title: Description status: type: string title: Status collected_at: anyOf: - type: string format: date-time - type: 'null' title: Collected At type: object required: - id - control_id - evidence_type - title - status title: EvidenceItem EvidenceReport: properties: case_id: type: string format: uuid title: Case Id title: type: string title: Title severity: type: string title: Severity status: type: string title: Status alert_count: type: integer title: Alert Count mitre_techniques: items: type: string type: array title: Mitre Techniques compliance_frameworks: items: type: string type: array title: Compliance Frameworks evidence_chain: items: additionalProperties: true type: object type: array title: Evidence Chain generated_at: type: string format: date-time title: Generated At type: object required: - case_id - title - severity - status - alert_count - mitre_techniques - compliance_frameworks - evidence_chain - generated_at title: EvidenceReport EvidenceResponse: properties: id: type: string format: uuid title: Id case_id: anyOf: - type: string format: uuid - type: 'null' title: Case Id framework: type: string title: Framework control_id: type: string title: Control Id control_title: anyOf: - type: string - type: 'null' title: Control Title evidence_kind: type: string title: Evidence Kind summary: type: string title: Summary raw_payload: additionalProperties: true type: object title: Raw Payload payload_hash: anyOf: - type: string - type: 'null' title: Payload Hash prev_hash: anyOf: - type: string - type: 'null' title: Prev Hash collected_at: type: string format: date-time title: Collected At reviewed_by: anyOf: - type: string - type: 'null' title: Reviewed By reviewed_at: anyOf: - type: string format: date-time - type: 'null' title: Reviewed At status: type: string title: Status created_at: type: string format: date-time title: Created At type: object required: - id - case_id - framework - control_id - control_title - evidence_kind - summary - raw_payload - payload_hash - prev_hash - collected_at - reviewed_by - reviewed_at - status - created_at title: EvidenceResponse ExecuteRuleRequest: properties: events: items: additionalProperties: true type: object type: array maxItems: 1000 title: Events description: Events to test the rule against type: object required: - events title: ExecuteRuleRequest description: Payload for ad-hoc rule execution. ExecuteRuleResponse: properties: rule_id: type: string title: Rule Id rule_name: type: string title: Rule Name rule_language: type: string title: Rule Language severity: type: string title: Severity matched: type: boolean title: Matched match_count: type: integer title: Match Count matched_events: items: additionalProperties: true type: object type: array title: Matched Events score: type: number title: Score error: anyOf: - type: string - type: 'null' title: Error execution_time_ms: type: number title: Execution Time Ms type: object required: - rule_id - rule_name - rule_language - severity - matched - match_count - matched_events - score - error - execution_time_ms title: ExecuteRuleResponse ExplainResponse: properties: run: $ref: '#/components/schemas/RunSummary' previous: anyOf: - $ref: '#/components/schemas/EventOut' - type: 'null' focus: $ref: '#/components/schemas/EventOut' next: anyOf: - $ref: '#/components/schemas/EventOut' - type: 'null' artifacts: items: $ref: '#/components/schemas/ArtifactDetail' type: array title: Artifacts type: object required: - run - previous - focus - next - artifacts title: ExplainResponse description: 'Why-did-the-agent-do-this view for a single step. Includes the focal event plus the immediately preceding event (the decision that led into this step) and the immediately following event (the decision the agent made afterwards). Artifacts attached to the focal event are inlined so the auditor sees the literal LLM transcript.' ExternalAssetType: type: string enum: - domain - subdomain - ip - cert - web_service - api_endpoint title: ExternalAssetType description: Types of externally-discovered assets. FanoutResult: properties: connector_id: type: string format: uuid title: Connector Id connector_type: type: string title: Connector Type connector_name: type: string title: Connector Name status: type: string title: Status description: ok | error | unsupported | skipped external_id: anyOf: - type: string - type: 'null' title: External Id external_url: anyOf: - type: string - type: 'null' title: External Url external_status: anyOf: - type: string - type: 'null' title: External Status error: anyOf: - type: string - type: 'null' title: Error type: object required: - connector_id - connector_type - connector_name - status title: FanoutResult description: 'Outcome of one ``push_case`` / ``push_status_change`` call. Always set so the UI can show "Jira ✓ · ServiceNow ✗ (timeout)" next to the case header without needing to re-read the audit log.' FederatedBackend: properties: connector_id: type: string format: uuid title: Connector Id connector_type: type: string title: Connector Type name: type: string title: Name health_status: type: string title: Health Status is_enabled: type: boolean title: Is Enabled type: object required: - connector_id - connector_type - name - health_status - is_enabled title: FederatedBackend description: Read-only projection of a connector instance eligible for fan-out. FederatedBackendsResponse: properties: backends: items: $ref: '#/components/schemas/FederatedBackend' type: array title: Backends type: object required: - backends title: FederatedBackendsResponse FederatedSearchRequest: properties: free_text: type: string maxLength: 2000 title: Free Text default: '' indicators: items: $ref: '#/components/schemas/IndicatorPayload' type: array title: Indicators since_seconds: type: integer maximum: 604800.0 exclusiveMinimum: 0.0 title: Since Seconds default: 3600 limit: type: integer maximum: 1000.0 exclusiveMinimum: 0.0 title: Limit default: 100 connector_ids: anyOf: - items: type: string format: uuid type: array - type: 'null' title: Connector Ids per_backend_timeout_seconds: anyOf: - type: number maximum: 120.0 exclusiveMinimum: 0.0 - type: 'null' title: Per Backend Timeout Seconds type: object title: FederatedSearchRequest description: 'Top-level federated search payload. ``connector_ids`` lets the caller scope the fan-out to specific instances (e.g. "only my prod Splunk, not the staging Sentinel"). Omit it to query every federated-capable connector the tenant has enabled. ``per_backend_timeout_seconds`` caps how long any one SIEM gets before we move on. Default is half the configured connectors-service timeout — long enough for a real SIEM round-trip, short enough that a single dead backend doesn''t block the merged answer.' FederatedSearchResponse: properties: rows: items: additionalProperties: true type: object type: array title: Rows row_count: type: integer title: Row Count sources: items: $ref: '#/components/schemas/SourceVerdict' type: array title: Sources truncated: type: boolean title: Truncated description: True when the merged row count was capped at ``limit`` after fan-out. default: false type: object required: - rows - row_count - sources title: FederatedSearchResponse FeedCreate: properties: name: type: string title: Name feed_type: type: string title: Feed Type url: anyOf: - type: string - type: 'null' title: Url api_key_ref: anyOf: - type: string - type: 'null' title: Api Key Ref poll_interval: type: integer title: Poll Interval default: 3600 config: additionalProperties: true type: object title: Config type: object required: - name - feed_type title: FeedCreate FeedIndicatorsResponse: properties: indicators: items: additionalProperties: true type: object type: array title: Indicators total: type: integer title: Total shown: type: integer title: Shown default: 0 bounded: type: boolean title: Bounded default: false source: type: string title: Source degraded: type: boolean title: Degraded default: false reason: type: string title: Reason default: '' type: object required: - indicators - total - source title: FeedIndicatorsResponse description: 'What the console''s `/threat-intel` page renders. ``source`` and ``degraded`` travel with the payload for the same reason they do on the connector catalog: an empty list because nothing has been collected yet and an empty list because the service is not deployed are different facts, and a reader cannot tell them apart from the rows alone.' FeedOut: properties: name: type: string title: Name feed_type: type: string title: Feed Type url: anyOf: - type: string - type: 'null' title: Url api_key_ref: anyOf: - type: string - type: 'null' title: Api Key Ref poll_interval: type: integer title: Poll Interval default: 3600 config: additionalProperties: true type: object title: Config id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id is_enabled: type: boolean title: Is Enabled last_polled_at: anyOf: - type: string - type: 'null' title: Last Polled At created_at: type: string title: Created At type: object required: - name - feed_type - id - tenant_id - is_enabled - created_at title: FeedOut FindingCreate: properties: cloud_provider: type: string title: Cloud Provider cloud_account: anyOf: - type: string - type: 'null' title: Cloud Account cloud_region: anyOf: - type: string - type: 'null' title: Cloud Region resource_type: type: string title: Resource Type resource_id: type: string title: Resource Id resource_name: anyOf: - type: string - type: 'null' title: Resource Name rule_id: type: string title: Rule Id rule_title: type: string title: Rule Title description: anyOf: - type: string - type: 'null' title: Description severity: type: string title: Severity default: medium frameworks: anyOf: - items: type: string type: array - type: 'null' title: Frameworks control_ids: anyOf: - items: type: string type: array - type: 'null' title: Control Ids evidence: additionalProperties: true type: object title: Evidence remediation_guide: anyOf: - type: string - type: 'null' title: Remediation Guide type: object required: - cloud_provider - resource_type - resource_id - rule_id - rule_title title: FindingCreate FindingOut: properties: cloud_provider: type: string title: Cloud Provider cloud_account: anyOf: - type: string - type: 'null' title: Cloud Account cloud_region: anyOf: - type: string - type: 'null' title: Cloud Region resource_type: type: string title: Resource Type resource_id: type: string title: Resource Id resource_name: anyOf: - type: string - type: 'null' title: Resource Name rule_id: type: string title: Rule Id rule_title: type: string title: Rule Title description: anyOf: - type: string - type: 'null' title: Description severity: type: string title: Severity default: medium frameworks: anyOf: - items: type: string type: array - type: 'null' title: Frameworks control_ids: anyOf: - items: type: string type: array - type: 'null' title: Control Ids evidence: additionalProperties: true type: object title: Evidence remediation_guide: anyOf: - type: string - type: 'null' title: Remediation Guide id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id status: type: string title: Status auto_remediated: type: boolean title: Auto Remediated first_detected_at: type: string title: First Detected At last_evaluated_at: type: string title: Last Evaluated At resolved_at: anyOf: - type: string - type: 'null' title: Resolved At type: object required: - cloud_provider - resource_type - resource_id - rule_id - rule_title - id - tenant_id - status - auto_remediated - first_detected_at - last_evaluated_at title: FindingOut FinishRequest: properties: credential: additionalProperties: true type: object title: Credential challenge: type: string maxLength: 512 minLength: 8 title: Challenge type: object required: - credential - challenge title: FinishRequest FormatsResponse: properties: formats: additionalProperties: type: string type: object title: Formats type: object required: - formats title: FormatsResponse FrameworkDetail: properties: framework: type: string title: Framework framework_name: type: string title: Framework Name summary: $ref: '#/components/schemas/FrameworkSummary' controls: items: $ref: '#/components/schemas/ControlWithEvidence' type: array title: Controls generated_at: type: string format: date-time title: Generated At type: object required: - framework - framework_name - summary - controls - generated_at title: FrameworkDetail FrameworkSummary: properties: total: type: integer title: Total collected: type: integer title: Collected review: type: integer title: Review approved: type: integer title: Approved rejected: type: integer title: Rejected missing: type: integer title: Missing pct: type: number title: Pct type: object required: - total - collected - review - approved - rejected - missing - pct title: FrameworkSummary FrameworksResponse: properties: frameworks: additionalProperties: additionalProperties: type: string type: object type: object title: Frameworks type: object required: - frameworks title: FrameworksResponse FreshnessSLOResponse: properties: status: type: string title: Status expected_cadence_seconds: type: integer title: Expected Cadence Seconds seconds_since_last_event: anyOf: - type: integer - type: 'null' title: Seconds Since Last Event category: type: string title: Category type: object required: - status - expected_cadence_seconds - seconds_since_last_event - category title: FreshnessSLOResponse description: 'Freshness verdict surfaced on every ``ConnectorResponse``. Driven by ``app.services.connector_freshness.compute_freshness``. The UI uses ``status`` to color the badge and ``seconds_since_last_event`` + ``expected_cadence_seconds`` to render the tooltip ("expected within 5 min, last event 12 min ago"). ``category`` is echoed back for client-side grouping without re-deriving it.' FrontendDetectionRule: properties: id: type: string title: Id name: type: string title: Name description: anyOf: - type: string - type: 'null' title: Description language: type: string title: Language body: type: string title: Body enabled: type: boolean title: Enabled default: true tags: items: type: string type: array title: Tags mitre: items: type: string type: array title: Mitre severity: type: string title: Severity default: medium createdAt: type: string title: Createdat updatedAt: type: string title: Updatedat lastTriggeredAt: anyOf: - type: string - type: 'null' title: Lasttriggeredat hitCount: type: integer title: Hitcount default: 0 isBuiltin: type: boolean title: Isbuiltin default: false sourceId: anyOf: - type: string - type: 'null' title: Sourceid tuned: type: boolean title: Tuned default: false timestampsFromArtefact: type: boolean title: Timestampsfromartefact default: false type: object required: - id - name - language - body - createdAt - updatedAt title: FrontendDetectionRule description: Mirrors `DetectionRule` in apps/web/src/lib/api.ts. FunnelDeltas: properties: events_of_interest: anyOf: - type: number - type: 'null' title: Events Of Interest correlation_instances: anyOf: - type: number - type: 'null' title: Correlation Instances alerts_generated: anyOf: - type: number - type: 'null' title: Alerts Generated signal_to_noise: anyOf: - type: number - type: 'null' title: Signal To Noise mttd_seconds: anyOf: - type: number - type: 'null' title: Mttd Seconds analyst_queue_depth: anyOf: - type: number - type: 'null' title: Analyst Queue Depth type: object title: FunnelDeltas description: 'Period-over-period percentage deltas for the funnel KPI bar. ``None`` means the previous window was empty — no baseline exists, and the UI renders that as "no baseline" instead of a misleading 0%.' FunnelMetrics: properties: period: type: string title: Period events_of_interest: type: integer title: Events Of Interest correlation_instances: type: integer title: Correlation Instances alerts_generated: type: integer title: Alerts Generated signal_to_noise: type: number title: Signal To Noise mttd_seconds: type: number title: Mttd Seconds analyst_queue_depth: type: integer title: Analyst Queue Depth correlation_efficiency: type: number title: Correlation Efficiency alert_yield: type: number title: Alert Yield mitre_coverage: $ref: '#/components/schemas/MitreCoverage' deltas: $ref: '#/components/schemas/FunnelDeltas' generated_at: type: string format: date-time title: Generated At repeat_alerts_suppressed: type: integer title: Repeat Alerts Suppressed default: 0 repeat_suppression_rate: type: number title: Repeat Suppression Rate default: 0.0 triaged_alerts: type: integer title: Triaged Alerts default: 0 abstentions: type: integer title: Abstentions default: 0 abstention_rate: type: number title: Abstention Rate default: 0.0 ungrounded_demotions: type: integer title: Ungrounded Demotions default: 0 mean_groundedness: anyOf: - type: number - type: 'null' title: Mean Groundedness scored_verdicts: type: integer title: Scored Verdicts default: 0 type: object required: - period - events_of_interest - correlation_instances - alerts_generated - signal_to_noise - mttd_seconds - analyst_queue_depth - correlation_efficiency - alert_yield - mitre_coverage - deltas - generated_at title: FunnelMetrics description: 'Tenant funnel + efficiency for the selected period. All counts are absolute for ``period``. ``signal_to_noise`` is the fraction of *resolved* alerts that ended in disposition ``true_positive`` (i.e. signal / (signal + noise)). ``mttd_seconds`` mirrors /metrics/soc''s MTTD definition (Alert.created_at → Alert.first_seen_at) but expressed in seconds for funnel-level precision. ``deltas`` compares this period to the immediately preceding period of the same length.' GateLogOut: properties: id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id action_id: type: string format: uuid title: Action Id action_type: type: string title: Action Type blast_radius: type: string title: Blast Radius maturity_tier: type: integer title: Maturity Tier decision: type: string title: Decision rationale: anyOf: - type: string - type: 'null' title: Rationale actor: type: string title: Actor created_at: type: string title: Created At type: object required: - id - tenant_id - action_id - action_type - blast_radius - maturity_tier - decision - rationale - actor - created_at title: GateLogOut GenerateRequest: properties: title: type: string title: Title report_type: type: string title: Report Type default: board_summary period_start: type: string format: date-time title: Period Start period_end: type: string format: date-time title: Period End output_format: type: string title: Output Format default: pdf recipients: anyOf: - items: type: string type: array - type: 'null' title: Recipients type: object required: - title - period_start - period_end title: GenerateRequest GrantListResponse: properties: tenant_id: type: string title: Tenant Id grants: items: $ref: '#/components/schemas/GrantModel' type: array title: Grants demoted_now: items: additionalProperties: true type: object type: array title: Demoted Now type: object required: - tenant_id - grants - demoted_now title: GrantListResponse GrantModel: properties: id: type: string title: Id scope_kind: type: string title: Scope Kind scope_key: type: string title: Scope Key capability: type: string title: Capability state: type: string title: State source: type: string title: Source is_override: type: boolean title: Is Override evidence: anyOf: - additionalProperties: true type: object - type: 'null' title: Evidence granted_at: anyOf: - type: string - type: 'null' title: Granted At demoted_at: anyOf: - type: string - type: 'null' title: Demoted At demoted_reason: anyOf: - type: string - type: 'null' title: Demoted Reason override_reason: anyOf: - type: string - type: 'null' title: Override Reason type: object required: - id - scope_kind - scope_key - capability - state - source - is_override title: GrantModel GraphEdge: properties: source: type: string title: Source target: type: string title: Target type: type: string title: Type type: object required: - source - target - type title: GraphEdge GraphNode: properties: id: type: string title: Id label: type: string title: Label properties: additionalProperties: true type: object title: Properties type: object required: - id - label title: GraphNode GraphOverviewResponse: properties: nodes: items: $ref: '#/components/schemas/OverviewNode' type: array title: Nodes edges: items: $ref: '#/components/schemas/OverviewEdge' type: array title: Edges generatedAt: type: string title: Generatedat truncated: type: boolean title: Truncated default: false nodeLimit: type: integer title: Nodelimit default: 400 edgeLimit: type: integer title: Edgelimit default: 900 type: object required: - nodes - edges - generatedAt title: GraphOverviewResponse description: 'The tenant''s entity graph, bounded for one canvas render. ``truncated`` is part of the contract rather than a diagnostic. A graph cut at the node ceiling and a graph that is genuinely this size render identically, and a viewer who cannot tell them apart reads a partial picture as the whole estate. ``nodeLimit`` and ``edgeLimit`` travel with it so the ceiling a caller reports is the ceiling this service applied. They are the same two constants the traversal is bounded by, read from ``graph_service`` rather than restated: a console that hardcoded 400 and 900 would keep printing those numbers after someone moved them here, and a client telling an analyst the wrong limit is a smaller version of the bug ``truncated`` exists to fix.' HTTPValidationError: properties: detail: items: $ref: '#/components/schemas/ValidationError' type: array title: Detail type: object title: HTTPValidationError HeatmapCell: properties: control_id: type: string title: Control Id title: type: string title: Title status: type: string title: Status evidence_count: type: integer title: Evidence Count type: object required: - control_id - title - status - evidence_count title: HeatmapCell HeatmapResponse: properties: framework: type: string title: Framework cells: items: $ref: '#/components/schemas/HeatmapCell' type: array title: Cells generated_at: type: string format: date-time title: Generated At type: object required: - framework - cells - generated_at title: HeatmapResponse HostedAgentScanRequest: properties: adminKey: type: string minLength: 8 title: Adminkey description: Provider admin key. Used for this call and not stored. type: object required: - adminKey title: HostedAgentScanRequest HuntPlanClause: properties: field: type: string maxLength: 64 title: Field operator: type: string maxLength: 16 title: Operator value: type: string maxLength: 512 title: Value type: object required: - field - operator - value title: HuntPlanClause description: 'One predicate of a hunting agent''s plan. Three typed strings and nothing else. There is no property here that can carry a query, a fragment of one, or free text, which is the property ``scripts/check_hunt_agent_boundary.py`` reads this model to check. The field and operator are validated against the closed sets in ``app.services.retro_hunt.hunt_plan_sql`` before anything is compiled.' HuntPlanExecuteRequest: properties: clauses: items: $ref: '#/components/schemas/HuntPlanClause' type: array maxItems: 7 minItems: 1 title: Clauses lookback_hours: type: integer maximum: 2160.0 minimum: 1.0 title: Lookback Hours default: 168 limit: type: integer maximum: 200.0 minimum: 1.0 title: Limit default: 200 type: object required: - clauses title: HuntPlanExecuteRequest HuntPreviewItem: properties: id: type: string title: Id timestamp: type: string title: Timestamp source: type: string title: Source severity: anyOf: - type: string - type: 'null' title: Severity fields: additionalProperties: true type: object title: Fields type: object required: - id - timestamp - source title: HuntPreviewItem HuntReportOut: properties: id: type: string title: Id source_kind: type: string title: Source Kind source_id: type: string title: Source Id case_id: anyOf: - type: string - type: 'null' title: Case Id title: type: string title: Title what: type: string title: What how: type: string title: How where: type: string title: Where when: type: string title: When citations: items: additionalProperties: true type: object type: array title: Citations match_count: type: integer title: Match Count generated_by: type: string title: Generated By created_at: type: string title: Created At type: object required: - id - source_kind - source_id - case_id - title - what - how - where - when - match_count - generated_by - created_at title: HuntReportOut HuntRequest: properties: rule_ids: anyOf: - items: type: string format: uuid type: array - type: 'null' title: Rule Ids description: Specific rule IDs to hunt with; omit to use all active rules rule_language: anyOf: - type: string - type: 'null' title: Rule Language description: Filter rules by language (sigma, yara, kql, eql) events: items: additionalProperties: true type: object type: array maxItems: 5000 title: Events description: Events to hunt through type: object required: - events title: HuntRequest description: Payload for threat hunting across events. HuntRunResponse: properties: id: type: string format: uuid title: Id hunt_id: type: string format: uuid title: Hunt Id run_at: type: string format: date-time title: Run At platform: type: string title: Platform query_used: anyOf: - type: string - type: 'null' title: Query Used hit_count: type: integer title: Hit Count result_sample: items: additionalProperties: true type: object type: array title: Result Sample duration_ms: anyOf: - type: integer - type: 'null' title: Duration Ms error: anyOf: - type: string - type: 'null' title: Error type: object required: - id - hunt_id - run_at - platform - query_used - hit_count - result_sample - duration_ms - error title: HuntRunResponse IOCCreate: properties: ioc_type: type: string title: Ioc Type value: type: string title: Value confidence: type: integer maximum: 100.0 minimum: 0.0 title: Confidence default: 50 severity: type: string title: Severity default: medium tlp: type: string title: Tlp default: amber threat_actor: anyOf: - type: string - type: 'null' title: Threat Actor campaign: anyOf: - type: string - type: 'null' title: Campaign malware_family: anyOf: - type: string - type: 'null' title: Malware Family tags: anyOf: - items: type: string type: array - type: 'null' title: Tags source: type: string title: Source default: internal source_ref: anyOf: - type: string - type: 'null' title: Source Ref expires_at: anyOf: - type: string format: date-time - type: 'null' title: Expires At linked_alerts: anyOf: - items: type: string format: uuid type: array - type: 'null' title: Linked Alerts context: additionalProperties: true type: object title: Context type: object required: - ioc_type - value title: IOCCreate IOCOut: properties: ioc_type: type: string title: Ioc Type value: type: string title: Value confidence: type: integer maximum: 100.0 minimum: 0.0 title: Confidence default: 50 severity: type: string title: Severity default: medium tlp: type: string title: Tlp default: amber threat_actor: anyOf: - type: string - type: 'null' title: Threat Actor campaign: anyOf: - type: string - type: 'null' title: Campaign malware_family: anyOf: - type: string - type: 'null' title: Malware Family tags: anyOf: - items: type: string type: array - type: 'null' title: Tags source: type: string title: Source default: internal source_ref: anyOf: - type: string - type: 'null' title: Source Ref expires_at: anyOf: - type: string format: date-time - type: 'null' title: Expires At linked_alerts: anyOf: - items: type: string format: uuid type: array - type: 'null' title: Linked Alerts context: additionalProperties: true type: object title: Context id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id is_active: type: boolean title: Is Active false_positive: type: boolean title: False Positive first_seen: type: string title: First Seen last_seen: type: string title: Last Seen created_at: type: string title: Created At type: object required: - ioc_type - value - id - tenant_id - is_active - false_positive - first_seen - last_seen - created_at title: IOCOut IdentityRecord: properties: source: type: string title: Source id: type: string title: Id email: anyOf: - type: string - type: 'null' title: Email email_verified: type: boolean title: Email Verified default: false upn: anyOf: - type: string - type: 'null' title: Upn employee_id: anyOf: - type: string - type: 'null' title: Employee Id display_name: anyOf: - type: string - type: 'null' title: Display Name maps_to: items: type: string type: array title: Maps To type: object required: - source - id title: IdentityRecord description: One source-scoped identity offered for resolution. IdentityTimeline: properties: timeline_id: type: string format: uuid title: Timeline Id identity_kind: type: string enum: - user - device - service_account - ip title: Identity Kind identity_value: type: string title: Identity Value from_ts: type: string format: date-time title: From Ts to_ts: type: string format: date-time title: To Ts events: items: $ref: '#/components/schemas/app__api__v1__endpoints__identity_timeline__TimelineEvent' type: array title: Events total_events: type: integer title: Total Events risk_score: anyOf: - type: number - type: 'null' title: Risk Score built_at: type: string format: date-time title: Built At sources_unavailable: items: type: string type: array title: Sources Unavailable type: object required: - timeline_id - identity_kind - identity_value - from_ts - to_ts - events - total_events - built_at title: IdentityTimeline InboundResult: properties: case_id: anyOf: - type: string format: uuid - type: 'null' title: Case Id case_number: anyOf: - type: string - type: 'null' title: Case Number external_id: anyOf: - type: string - type: 'null' title: External Id old_status: anyOf: - type: string - type: 'null' title: Old Status new_status: anyOf: - type: string - type: 'null' title: New Status status_changed: type: boolean title: Status Changed note: anyOf: - type: string - type: 'null' title: Note type: object required: - case_id - case_number - external_id - old_status - new_status - status_changed title: InboundResult description: Response body for the inbound webhook. InboxTemplateInfo: properties: template_id: type: string title: Template Id label: type: string title: Label description: type: string title: Description category: type: string title: Category type: object required: - template_id - label - description - category title: InboxTemplateInfo description: Catalog entry for a vendor template the operator can pick. InboxTokenCreate: properties: template_id: type: string title: Template Id description: Vendor template stem from ``services/ingest/internal/normalizer/templates/*.yaml`` (e.g. ``pagerduty``, ``generic-json``). label: anyOf: - type: string - type: 'null' title: Label description: Operator-facing label ("PagerDuty on-call"). hmac_secret: anyOf: - type: string maxLength: 512 minLength: 16 - type: 'null' title: Hmac Secret description: Optional HMAC-SHA256 secret for ``X-Signature`` verification. If provided, the ingest service rejects requests whose signature doesn't match. NULL means the URL token is the only authenticator. type: object required: - template_id title: InboxTokenCreate description: Request body for ``POST /api/v1/inbox/tokens``. InboxTokenListItem: properties: fingerprint: type: string title: Fingerprint template_id: type: string title: Template Id label: anyOf: - type: string - type: 'null' title: Label has_hmac_secret: type: boolean title: Has Hmac Secret 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 type: object required: - fingerprint - template_id - label - has_hmac_secret - created_at - revoked_at - last_used_at title: InboxTokenListItem description: 'List view: omits the plaintext token.' InboxTokenResponse: properties: token: type: string title: Token inbox_url: type: string title: Inbox Url template_id: type: string title: Template Id label: anyOf: - type: string - type: 'null' title: Label has_hmac_secret: type: boolean title: Has Hmac Secret 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 type: object required: - token - inbox_url - template_id - label - has_hmac_secret - created_at - revoked_at - last_used_at title: InboxTokenResponse description: 'A single inbox token, including the *plaintext* token + URL. The token is returned in full **only** at mint time (when the wizard needs to show it to the operator). Subsequent list calls return the fingerprint instead, which is enough to identify the row but not enough to forge inbound traffic.' IncidentContextResponse: properties: alert_id: type: string title: Alert Id tenant_id: type: string title: Tenant Id identities: items: additionalProperties: true type: object type: array title: Identities assets: items: additionalProperties: true type: object type: array title: Assets cloud: items: additionalProperties: true type: object type: array title: Cloud business: items: additionalProperties: true type: object type: array title: Business threat: items: additionalProperties: true type: object type: array title: Threat dimensions_resolved: type: integer title: Dimensions Resolved default: 0 partial: type: boolean title: Partial default: false errors: items: type: string type: array title: Errors narrative: items: type: string type: array title: Narrative type: object required: - alert_id - tenant_id title: IncidentContextResponse description: 'The five context dimensions for one alert. ``partial`` and ``errors`` are part of the contract, not diagnostics. An empty bundle from an unreachable graph and an empty bundle from an alert with genuinely no context look identical otherwise, and a consumer that cannot tell them apart will treat "we could not look" as "there is nothing to find".' IndicatorCreate: properties: profile_id: type: string format: uuid title: Profile Id indicator_type: type: string title: Indicator Type severity: type: string title: Severity default: medium description: type: string title: Description source_alert_id: anyOf: - type: string format: uuid - type: 'null' title: Source Alert Id evidence: additionalProperties: true type: object title: Evidence occurred_at: anyOf: - type: string format: date-time - type: 'null' title: Occurred At type: object required: - profile_id - indicator_type - description title: IndicatorCreate IndicatorListResponse: properties: items: items: $ref: '#/components/schemas/STIXIndicator' type: array title: Items total: type: integer title: Total type: object required: - items - total title: IndicatorListResponse IndicatorOut: properties: id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id profile_id: type: string format: uuid title: Profile Id indicator_type: type: string title: Indicator Type severity: type: string title: Severity description: type: string title: Description source_alert_id: anyOf: - type: string format: uuid - type: 'null' title: Source Alert Id evidence: additionalProperties: true type: object title: Evidence occurred_at: type: string title: Occurred At acknowledged_at: anyOf: - type: string - type: 'null' title: Acknowledged At type: object required: - id - tenant_id - profile_id - indicator_type - severity - description - source_alert_id - evidence - occurred_at - acknowledged_at title: IndicatorOut IndicatorPayload: properties: field: type: string maxLength: 200 minLength: 1 title: Field operator: type: string maxLength: 20 minLength: 1 title: Operator value: title: Value type: object required: - field - operator - value title: IndicatorPayload description: 'One ``field value`` triple in a federated query. Mirrors ``services.connectors.app.federated.query.Indicator`` but expressed as Pydantic so FastAPI can render OpenAPI for the wizard + the SDK. The connectors microservice re-validates with ``parse_unified_query`` before any translator runs, so this model is intentionally permissive on ``value``.' IngestRequest: properties: title: type: string minLength: 2 title: Title content: type: string minLength: 10 title: Content doc_kind: type: string enum: - runbook - policy - playbook - sop - wiki - other title: Doc Kind default: runbook source_url: anyOf: - type: string - type: 'null' title: Source Url tags: items: type: string type: array title: Tags type: object required: - title - content title: IngestRequest IngestTokenResponse: properties: connector_id: type: string format: uuid title: Connector Id ingest_token: type: string title: Ingest Token inbox_url: type: string title: Inbox Url type: object required: - connector_id - ingest_token - inbox_url title: IngestTokenResponse description: Push-token response. ``inbox_url`` is the absolute endpoint. InitialAdminUserWire: properties: id: type: string title: Id email: type: string title: Email role: type: string title: Role type: object required: - id - email - role title: InitialAdminUserWire InsightSparkline: properties: points: items: type: number type: array title: Points description: Bucket values, oldest → newest. Length == 24. type: object title: InsightSparkline description: A bucketed time-series the UI renders as a tiny inline SVG. InsightTile: properties: key: type: string title: Key description: Stable identifier — used as a React key. label: type: string title: Label description: Human-readable tile heading. value: type: number title: Value description: 'Headline metric for the current window. Read it with `sample_count`: a mean over zero rows is reported as 0.0 and is *not* a measurement. The mean is not nullable because that is a breaking response change for every existing client, which is the same decision `/metrics/soc` took with `mttd_sample_count`.' sample_count: anyOf: - type: integer - type: 'null' title: Sample Count description: Rows behind `value`. A mean over three and over three thousand differ. unit: type: string title: Unit description: Display unit. The UI picks formatting per unit (``hours`` → 1 decimal, ``pct`` → percentage, ``usd`` → currency, ``count`` → integer, ``hours_saved`` → integer). previous_value: type: number title: Previous Value description: Value of the same metric over the immediately preceding window of equal length. Used to compute the delta. delta_pct: anyOf: - type: number - type: 'null' title: Delta Pct description: Percentage change vs ``previous_value``. ``None`` when the previous window had no data (so the percent change is undefined rather than misleadingly infinite). sparkline: $ref: '#/components/schemas/InsightSparkline' type: object required: - key - label - value - unit - previous_value title: InsightTile description: One tile on the SOC Insights dashboard. InstallRequest: properties: type: type: string enum: - detection - playbook - plugin title: Type id: type: string maxLength: 200 minLength: 1 title: Id type: object required: - type - id title: InstallRequest InstallResponse: properties: id: type: string title: Id type: type: string title: Type name: type: string title: Name version: type: string title: Version content_sha256: type: string title: Content Sha256 installed_at: type: string title: Installed At installed_by: type: string title: Installed By already_installed: type: boolean title: Already Installed default: false type: object required: - id - type - name - version - content_sha256 - installed_at - installed_by title: InstallResponse InvestigateRequest: properties: alert_summary: anyOf: - type: string - type: 'null' title: Alert Summary default: '' type: object title: InvestigateRequest InvestigationToolRequest: properties: tool: type: string title: Tool args: additionalProperties: true type: object title: Args type: object required: - tool title: InvestigationToolRequest description: 'One typed investigation pivot. ``tool`` names a primitive; ``args`` are its typed arguments. Deliberately not SQL: handing a model the lake query endpoint puts prompt-injectable text one step from the query planner, and the tenant predicate is the only thing between two customers'' data. ``tenant_id`` is taken from the authenticated session and any value supplied here is discarded.' KBChunk: properties: doc_id: type: string format: uuid title: Doc Id title: type: string title: Title chunk_index: type: integer title: Chunk Index content: type: string title: Content score: anyOf: - type: number - type: 'null' title: Score type: object required: - doc_id - title - chunk_index - content title: KBChunk KBDocResponse: properties: id: type: string format: uuid title: Id title: type: string title: Title doc_kind: type: string title: Doc Kind source_url: anyOf: - type: string - type: 'null' title: Source Url tags: items: type: string type: array title: Tags chunk_index: type: integer title: Chunk Index chunk_total: type: integer title: Chunk Total content_preview: type: string title: Content Preview created_at: type: string format: date-time title: Created At created_by: anyOf: - type: string - type: 'null' title: Created By type: object required: - id - title - doc_kind - source_url - tags - chunk_index - chunk_total - content_preview - created_at - created_by title: KBDocResponse KillSwitchState: properties: engaged: type: boolean title: Engaged scope: anyOf: - type: string - type: 'null' title: Scope description: global | tenant, when engaged reason: anyOf: - type: string - type: 'null' title: Reason engaged_by: anyOf: - type: string - type: 'null' title: Engaged By engaged_at: anyOf: - type: string format: date-time - type: 'null' title: Engaged At type: object required: - engaged title: KillSwitchState KpiBarTargetsOut: properties: false_positive_rate_max_pct: type: number maximum: 100.0 minimum: 0.0 title: False Positive Rate Max Pct alert_to_incident_ratio_min: type: number minimum: 1.0 title: Alert To Incident Ratio Min mitre_technique_tagging_min_pct: type: number maximum: 100.0 minimum: 0.0 title: Mitre Technique Tagging Min Pct mitre_subtechnique_tagging_min_pct: type: number maximum: 100.0 minimum: 0.0 title: Mitre Subtechnique Tagging Min Pct type: object required: - false_positive_rate_max_pct - alert_to_incident_ratio_min - mitre_technique_tagging_min_pct - mitre_subtechnique_tagging_min_pct title: KpiBarTargetsOut KpiBarTargetsUpdate: properties: false_positive_rate_max_pct: anyOf: - type: number maximum: 100.0 minimum: 0.0 - type: 'null' title: False Positive Rate Max Pct alert_to_incident_ratio_min: anyOf: - type: number minimum: 1.0 - type: 'null' title: Alert To Incident Ratio Min mitre_technique_tagging_min_pct: anyOf: - type: number maximum: 100.0 minimum: 0.0 - type: 'null' title: Mitre Technique Tagging Min Pct mitre_subtechnique_tagging_min_pct: anyOf: - type: number maximum: 100.0 minimum: 0.0 - type: 'null' title: Mitre Subtechnique Tagging Min Pct type: object title: KpiBarTargetsUpdate LLMProvider: type: string enum: - openai - anthropic - local-ollama - local-vllm - azure-openai - none title: LLMProvider LakeColumnInfo: properties: name: type: string title: Name type: type: string title: Type comment: type: string title: Comment default: '' type: object required: - name - type title: LakeColumnInfo description: One column within a lake table. LakeEstimateRequest: properties: sql: type: string maxLength: 200000 minLength: 1 title: Sql row_cap: anyOf: - type: integer maximum: 100000.0 minimum: 1.0 - type: 'null' title: Row Cap type: object required: - sql title: LakeEstimateRequest description: Ask what a query would read, without running it. LakeEstimateResponse: properties: estimated_rows: type: integer title: Estimated Rows estimated_parts: type: integer title: Estimated Parts estimated_bytes: type: integer title: Estimated Bytes cold_bytes: type: integer title: Cold Bytes warm_bytes: type: integer title: Warm Bytes touches_cold: type: boolean title: Touches Cold tables: items: type: string type: array title: Tables estimated_usd: anyOf: - type: number - type: 'null' title: Estimated Usd cost_basis: type: string title: Cost Basis type: object required: - estimated_rows - estimated_parts - estimated_bytes - cold_bytes - warm_bytes - touches_cold - tables - estimated_usd - cost_basis title: LakeEstimateResponse LakeQueryJobRequest: properties: sql: type: string maxLength: 200000 minLength: 1 title: Sql row_cap: anyOf: - type: integer maximum: 100000.0 minimum: 1.0 - type: 'null' title: Row Cap type: object required: - sql title: LakeQueryJobRequest LakeQueryJobResponse: properties: id: type: string format: uuid title: Id status: type: string title: Status referenced_tables: items: type: string type: array title: Referenced Tables row_cap: type: integer title: Row Cap estimate: additionalProperties: true type: object title: Estimate rows_read: type: integer title: Rows Read bytes_read: type: integer title: Bytes Read total_rows_approx: type: integer title: Total Rows Approx progress_ratio: anyOf: - type: number - type: 'null' title: Progress Ratio columns: anyOf: - items: type: string type: array - type: 'null' title: Columns rows: anyOf: - items: items: {} type: array type: array - type: 'null' title: Rows row_count: anyOf: - type: integer - type: 'null' title: Row Count error: anyOf: - type: string - type: 'null' title: Error 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 - status - referenced_tables - row_cap - estimate - rows_read - bytes_read - total_rows_approx - progress_ratio - columns - rows - row_count - error - created_at - started_at - finished_at title: LakeQueryJobResponse description: A job's state. The fields a console needs to show progress honestly. LakeQueryRequest: properties: sql: type: string maxLength: 200000 minLength: 1 title: Sql description: 'SELECT query to run against the warm tier. The server enforces a single-statement, allowlisted-tables-only policy and injects ``tenant_id`` predicates before execution. Use ``GET /api/v1/lake/schema`` to discover available tables and columns. Allowed tables: aisoc.alert_metrics, aisoc.ioc_enrichments, aisoc.raw_events.' row_cap: anyOf: - type: integer maximum: 100000.0 minimum: 1.0 - type: 'null' title: Row Cap description: Optional ``LIMIT`` clamp; defaults to 10000, hard-capped at 100000. timeout_seconds: anyOf: - type: number maximum: 60.0 exclusiveMinimum: 0.0 - type: 'null' title: Timeout Seconds description: Optional wall-clock cap. Defaults to the server cap (30s). Useful for agent loops that need to fail fast. force_sync: type: boolean title: Force Sync description: 'Run synchronously even when the estimate says the query must read the cold volume. Off by default: the synchronous path is bounded by the 30-second default and 60-second maximum above, and a cold read that does not fit inside them used to come back as a bare timeout with no indication of how much data was involved. Set this only when you already know the scan is small.' default: false type: object required: - sql title: LakeQueryRequest description: 'Request body for ``POST /api/v1/lake/sql``. Only ``sql`` is required. ``row_cap`` lets an operator request a smaller-than-default result set (e.g. for paginated UIs); the rewriter clamps to :data:`MAX_ROW_CAP` regardless. ``timeout_seconds`` likewise lets the caller fail fast — useful for agent loops where a 30-second hang would compound into a wall-clock budget overrun.' LakeQueryResponse: properties: columns: items: type: string type: array title: Columns rows: items: items: {} type: array type: array title: Rows row_count: type: integer title: Row Count row_cap: type: integer title: Row Cap referenced_tables: items: type: string type: array title: Referenced Tables elapsed_ms: type: integer title: Elapsed Ms executed_at: type: string format: date-time title: Executed At type: object required: - columns - rows - row_count - row_cap - referenced_tables - elapsed_ms - executed_at title: LakeQueryResponse description: 'Response body for ``POST /api/v1/lake/sql``. ``referenced_tables`` is the set of allowlisted tables the rewriter actually touched — it''s surfaced for clients that want to flag "you queried raw_events" in a UI without re-parsing the SQL themselves.' LakeSchemaResponse: properties: tables: items: $ref: '#/components/schemas/LakeTableInfo' type: array title: Tables type: object required: - tables title: LakeSchemaResponse description: 'Response body for ``GET /api/v1/lake/schema``. Wraps the table list in an envelope so we can extend the response later (e.g. with retention info or row-count estimates) without a breaking change.' LakeTableInfo: properties: table: type: string title: Table columns: items: $ref: '#/components/schemas/LakeColumnInfo' type: array title: Columns type: object required: - table - columns title: LakeTableInfo description: One allowlisted lake table, with its columns. LastEventResponse: properties: connector_id: type: string format: uuid title: Connector Id last_event_at: anyOf: - type: string format: date-time - type: 'null' title: Last Event At last_event_kind: anyOf: - type: string - type: 'null' title: Last Event Kind events_ingested: type: integer title: Events Ingested last_sync: anyOf: - type: string format: date-time - type: 'null' title: Last Sync health_status: type: string title: Health Status data_flowing: type: boolean title: Data Flowing type: object required: - connector_id - last_event_at - last_event_kind - events_ingested - last_sync - health_status - data_flowing title: LastEventResponse description: 'Lightweight payload for the verify-data-flowing screen. The onboarding flow polls this every few seconds while waiting for the first event to land. Keeping the shape minimal avoids paying the full ``ConnectorResponse`` cost on every poll tick.' LimitHeadroomOut: properties: key: type: string title: Key label: type: string title: Label used: type: integer title: Used limit: anyOf: - type: integer - type: 'null' title: Limit remaining: anyOf: - type: integer - type: 'null' title: Remaining pct_used: anyOf: - type: number - type: 'null' title: Pct Used state: type: string title: State description: unlimited | ok | warning | exhausted type: object required: - key - label - used - limit - remaining - pct_used - state title: LimitHeadroomOut ListResponse: properties: rules: items: $ref: '#/components/schemas/FrontendDetectionRule' type: array title: Rules total: type: integer title: Total builtinTotal: type: integer title: Builtintotal default: 0 customTotal: type: integer title: Customtotal default: 0 returned: type: integer title: Returned default: 0 offset: type: integer title: Offset default: 0 limit: type: integer title: Limit default: 0 catalogError: anyOf: - type: string - type: 'null' title: Catalogerror missingArtefacts: items: type: string type: array title: Missingartefacts type: object required: - rules - total title: ListResponse LlmCredentialTestResult: properties: outcome: type: string enum: - ok - refused - unreachable - unverified title: Outcome detail: type: string title: Detail provider: type: string title: Provider model: anyOf: - type: string - type: 'null' title: Model latency_ms: anyOf: - type: integer - type: 'null' title: Latency Ms type: object required: - outcome - detail - provider title: LlmCredentialTestResult description: 'What a real call to the configured provider did. Four outcomes, and `unverified` is one of them on purpose: an air-gapped deployment refuses the egress rather than attempting it, and reporting that as a failure would send an operator to debug a credential that is fine.' LlmCredentialUpsert: properties: provider: type: string enum: - openai - anthropic - azure-openai - local-ollama - local-vllm - local-litellm - custom title: Provider base_url: anyOf: - type: string maxLength: 512 - type: 'null' title: Base Url model: anyOf: - type: string maxLength: 120 - type: 'null' title: Model api_key: anyOf: - type: string maxLength: 4096 - type: 'null' title: Api Key settings: anyOf: - additionalProperties: true type: object - type: 'null' title: Settings enabled: type: boolean title: Enabled default: true type: object required: - provider title: LlmCredentialUpsert description: 'Payload for ``PUT /api/v1/llm/credentials``. ``api_key`` is intentionally optional. Sending ``null`` (or omitting it) on an *existing* row keeps the stored ciphertext untouched — that lets operators flip ``enabled``, swap ``model``, or change the ``base_url`` without re-typing a 100-character secret. On a *new* row, ``api_key`` is required when the chosen provider mandates one (we surface a 422 in that case rather than silently storing NULL).' LlmCredentialView: properties: provider: type: string enum: - openai - anthropic - azure-openai - local-ollama - local-vllm - local-litellm - custom title: Provider base_url: anyOf: - type: string - type: 'null' title: Base Url model: anyOf: - type: string - type: 'null' title: Model has_api_key: type: boolean title: Has Api Key settings: additionalProperties: true type: object title: Settings 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_rotated_at: anyOf: - type: string format: date-time - type: 'null' title: Last Rotated At type: object required: - provider - base_url - model - has_api_key - settings - enabled - created_at - updated_at - last_rotated_at title: LlmCredentialView description: 'Read-only projection of :class:`TenantLlmCredential`. The plaintext key is intentionally absent. ``has_api_key`` is the only signal we expose — same convention the OAuth credential view uses (``has_secret``).' LlmModelDiscoveryRequest: properties: base_url: anyOf: - type: string maxLength: 512 - type: 'null' title: Base Url api_key: anyOf: - type: string maxLength: 4096 - type: 'null' title: Api Key type: object title: LlmModelDiscoveryRequest description: 'Where to list models from, for a form that may not be saved yet. Both fields are optional. ``base_url`` falls back to the stored ``custom`` credential''s. ``api_key`` is used for this one request and never stored; when it is omitted the stored key is used, but only if the target is the stored base URL -- see :func:`discover_llm_models`.' LlmModelDiscoveryResult: properties: outcome: type: string enum: - ok - refused - unreachable - unverified title: Outcome detail: type: string title: Detail models: items: $ref: '#/components/schemas/DiscoveredModelView' type: array title: Models listed: type: integer title: Listed excluded: type: integer title: Excluded truncated: type: boolean title: Truncated type: object required: - outcome - detail - models - listed - excluded - truncated title: LlmModelDiscoveryResult description: 'The chat models an endpoint listed, or why there are none. ``outcome`` uses the Test button''s vocabulary. Anything other than ``ok`` leaves ``models`` empty and the console falls back to the hand-typed id, which is the path that always worked; nothing is invented to fill the gap.' LoginRequest: properties: email: type: string format: email title: Email password: type: string title: Password type: object required: - email - password title: LoginRequest LookupRequest: properties: sha256: type: string title: Sha256 description: The file's SHA-256 digest. Nothing but the digest is sent. provider: anyOf: - type: string - type: 'null' title: Provider type: object required: - sha256 title: LookupRequest MarketplaceItemSummary: properties: id: type: string title: Id type: type: string title: Type name: type: string title: Name description: type: string title: Description default: '' version: type: string title: Version default: 1.0.0 severity: anyOf: - type: string - type: 'null' title: Severity category: anyOf: - type: string - type: 'null' title: Category source: type: string title: Source default: core verified: type: boolean title: Verified default: false sdks: items: type: string type: array title: Sdks default: [] mitre_techniques: items: type: string type: array title: Mitre Techniques default: [] tags: items: type: string type: array title: Tags default: [] type: object required: - id - type - name title: MarketplaceItemSummary MarketplaceListResponse: properties: total: type: integer title: Total stats: additionalProperties: true type: object title: Stats mitre_coverage: additionalProperties: true type: object title: Mitre Coverage items: items: $ref: '#/components/schemas/MarketplaceItemSummary' type: array title: Items installed_ids: items: type: string type: array title: Installed Ids type: object required: - total - stats - mitre_coverage - items - installed_ids title: MarketplaceListResponse MaturityConfig: properties: maturity_tier: type: integer maximum: 4.0 minimum: 0.0 title: Maturity Tier action_overrides: additionalProperties: true type: object title: Action Overrides type: object required: - maturity_tier title: MaturityConfig MaturityOut: properties: id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id maturity_tier: type: integer title: Maturity Tier action_overrides: additionalProperties: true type: object title: Action Overrides changed_at: type: string title: Changed At created_at: type: string title: Created At type: object required: - id - tenant_id - maturity_tier - action_overrides - changed_at - created_at title: MaturityOut McpApprovalDecisionRequest: properties: approve: type: boolean title: Approve note: type: string maxLength: 500 title: Note default: '' type: object required: - approve title: McpApprovalDecisionRequest McpApprovalModel: properties: id: type: string title: Id tool_name: type: string title: Tool Name argument_fingerprint: type: string title: Argument Fingerprint requested_by: type: string title: Requested By state: type: string title: State decided_by: anyOf: - type: string - type: 'null' title: Decided By decision_note: type: string title: Decision Note expires_at: type: string format: date-time title: Expires At created_at: type: string format: date-time title: Created At type: object required: - id - tool_name - argument_fingerprint - requested_by - state - decided_by - decision_note - expires_at - created_at title: McpApprovalModel McpAuthorizeRequest: properties: tool_name: type: string maxLength: 120 minLength: 1 title: Tool Name arguments: additionalProperties: true type: object title: Arguments type: object required: - tool_name title: McpAuthorizeRequest description: 'What ``services/mcp`` asks before dispatching. ``arguments`` is sent in full rather than as key names because the approval fingerprint has to bind to the specific call. It is hashed on arrival and never stored, and the audit row records the argument *names* only — the values routinely carry hostnames and lake queries.' McpAuthorizeResponse: properties: call_id: type: string title: Call Id outcome: type: string title: Outcome reason: type: string title: Reason approval_id: anyOf: - type: string - type: 'null' title: Approval Id required_permission: type: string title: Required Permission default: '' type: object required: - call_id - outcome - reason title: McpAuthorizeResponse McpCompleteRequest: properties: outcome: type: string title: Outcome latency_ms: type: integer maximum: 86400000.0 minimum: 0.0 title: Latency Ms error_kind: anyOf: - type: string maxLength: 120 - type: 'null' title: Error Kind type: object required: - outcome - latency_ms title: McpCompleteRequest McpServerCreate: properties: name: type: string maxLength: 40 minLength: 2 title: Name label: anyOf: - type: string maxLength: 200 - type: 'null' title: Label transport: type: string title: Transport default: streamable_http url: anyOf: - type: string maxLength: 2048 - type: 'null' title: Url command: anyOf: - type: string maxLength: 512 - type: 'null' title: Command args: items: type: string type: array maxItems: 32 title: Args credential: anyOf: - additionalProperties: true type: object - type: 'null' title: Credential tool_allowlist: items: type: string type: array title: Tool Allowlist timeout_seconds: type: integer title: Timeout Seconds default: 20 max_response_bytes: type: integer title: Max Response Bytes default: 65536 enabled: type: boolean title: Enabled default: false type: object required: - name title: McpServerCreate McpServerListResponse: properties: servers: items: $ref: '#/components/schemas/McpServerModel' type: array title: Servers read_only_by_default: type: boolean title: Read Only By Default default: true type: object required: - servers title: McpServerListResponse McpServerModel: properties: id: type: string title: Id name: type: string title: Name label: anyOf: - type: string - type: 'null' title: Label transport: type: string title: Transport url: anyOf: - type: string - type: 'null' title: Url command: anyOf: - type: string - type: 'null' title: Command args: items: type: string type: array title: Args tool_allowlist: items: type: string type: array title: Tool Allowlist timeout_seconds: type: integer title: Timeout Seconds max_response_bytes: type: integer title: Max Response Bytes enabled: type: boolean title: Enabled has_credential: type: boolean title: Has Credential created_at: anyOf: - type: string - type: 'null' title: Created At updated_at: anyOf: - type: string - type: 'null' title: Updated At type: object required: - id - name - transport - timeout_seconds - max_response_bytes - enabled - has_credential title: McpServerModel description: What the console sees. Never the credential. McpServerUpdate: properties: label: anyOf: - type: string maxLength: 200 - type: 'null' title: Label transport: anyOf: - type: string - type: 'null' title: Transport url: anyOf: - type: string maxLength: 2048 - type: 'null' title: Url command: anyOf: - type: string maxLength: 512 - type: 'null' title: Command args: anyOf: - items: type: string type: array maxItems: 32 - type: 'null' title: Args credential: anyOf: - additionalProperties: true type: object - type: 'null' title: Credential tool_allowlist: anyOf: - items: type: string type: array - type: 'null' title: Tool Allowlist timeout_seconds: anyOf: - type: integer - type: 'null' title: Timeout Seconds max_response_bytes: anyOf: - type: integer - type: 'null' title: Max Response Bytes enabled: anyOf: - type: boolean - type: 'null' title: Enabled type: object title: McpServerUpdate McpToolGrantRequest: properties: enabled: type: boolean title: Enabled type: object required: - enabled title: McpToolGrantRequest McpToolStateModel: properties: name: type: string title: Name read_only: type: boolean title: Read Only enabled: type: boolean title: Enabled source: type: string title: Source required_permission: type: string title: Required Permission requires_approval: type: boolean title: Requires Approval decided_by: type: string title: Decided By type: object required: - name - read_only - enabled - source - required_permission - requires_approval - decided_by title: McpToolStateModel MemberOut: properties: user_id: type: string format: uuid title: User Id email: type: string title: Email org_role: type: string title: Org Role granted_tenants: items: type: string format: uuid type: array title: Granted Tenants type: object required: - user_id - email - org_role - granted_tenants title: MemberOut MemberUpsert: properties: user_id: type: string format: uuid title: User Id org_role: type: string title: Org Role default: viewer type: object required: - user_id title: MemberUpsert MemoryConflictOut: properties: left: $ref: '#/components/schemas/MemoryItemOut' right: anyOf: - $ref: '#/components/schemas/MemoryItemOut' - type: 'null' type: object required: - left - right title: MemoryConflictOut MemoryConflictResolution: properties: keep: type: string title: Keep note: type: string maxLength: 500 title: Note default: '' type: object required: - keep title: MemoryConflictResolution MemoryIngestResult: properties: item: $ref: '#/components/schemas/MemoryItemOut' outcome: type: string title: Outcome indexed: type: boolean title: Indexed index_error: anyOf: - type: string - type: 'null' title: Index Error type: object required: - item - outcome - indexed title: MemoryIngestResult MemoryItemIn: properties: kind: type: string title: Kind description: case | runbook | memory subject: type: string maxLength: 400 minLength: 1 title: Subject content: type: string maxLength: 200000 minLength: 1 title: Content title: type: string maxLength: 400 title: Title default: '' source_ref: type: string maxLength: 400 title: Source Ref default: '' source: type: string maxLength: 60 title: Source default: analyst scope: type: string title: Scope default: tenant expires_at: anyOf: - type: string format: date-time - type: 'null' title: Expires At type: object required: - kind - subject - content title: MemoryItemIn MemoryItemOut: properties: id: type: string title: Id kind: type: string title: Kind subject: type: string title: Subject title: type: string title: Title content: type: string title: Content source_ref: type: string title: Source Ref author: type: string title: Author source: type: string title: Source scope: type: string title: Scope state: type: string title: State state_reason: type: string title: State Reason conflicts_with: anyOf: - type: string - type: 'null' title: Conflicts With expires_at: anyOf: - type: string format: date-time - type: 'null' title: Expires At embedding_model: anyOf: - type: string - type: 'null' title: Embedding Model embedded_at: anyOf: - type: string format: date-time - type: 'null' title: Embedded At created_at: type: string format: date-time title: Created At updated_at: type: string format: date-time title: Updated At type: object required: - id - kind - subject - title - content - source_ref - author - source - scope - state - state_reason - conflicts_with - expires_at - embedding_model - embedded_at - created_at - updated_at title: MemoryItemOut MemoryRecallChunk: properties: item_id: type: string format: uuid title: Item Id kind: type: string title: Kind subject: type: string title: Subject title: type: string title: Title content: type: string title: Content score: type: number title: Score author: type: string title: Author source: type: string title: Source type: object required: - item_id - kind - subject - title - content - score - author - source title: MemoryRecallChunk description: 'One semantically recalled memory item, with the provenance to weigh it. A separate type from ``KBChunk`` on purpose. These come from a different store, by a different retrieval, with an author and a source attached, and flattening them into the same list would let a reader take a similarity hit for a full-text match.' MemoryRecallHit: properties: item: $ref: '#/components/schemas/MemoryItemOut' score: type: number title: Score type: object required: - item - score title: MemoryRecallHit MemoryRecallIn: properties: query: type: string maxLength: 4000 minLength: 1 title: Query kinds: items: type: string type: array title: Kinds limit: type: integer maximum: 50.0 minimum: 1.0 title: Limit default: 5 type: object required: - query title: MemoryRecallIn MergeRequest: properties: primary_id: type: string format: uuid title: Primary Id duplicate_ids: items: type: string format: uuid type: array maxItems: 200 minItems: 1 title: Duplicate Ids reason: type: string maxLength: 500 title: Reason default: '' type: object required: - primary_id - duplicate_ids title: MergeRequest MergeResponse: properties: primary_id: type: string title: Primary Id merged_ids: items: type: string type: array title: Merged Ids case_id: anyOf: - type: string - type: 'null' title: Case Id type: object required: - primary_id - merged_ids - case_id title: MergeResponse MetricsOut: properties: tenant_id: type: string format: uuid title: Tenant Id snapshot_at: type: string title: Snapshot At open_alerts: type: integer title: Open Alerts critical_alerts: type: integer title: Critical Alerts open_cases: type: integer title: Open Cases mttr_minutes: anyOf: - type: number - type: 'null' title: Mttr Minutes sla_breaches: type: integer title: Sla Breaches connector_count: type: integer title: Connector Count health_score: anyOf: - type: number - type: 'null' title: Health Score type: object required: - tenant_id - snapshot_at - open_alerts - critical_alerts - open_cases - mttr_minutes - sla_breaches - connector_count - health_score title: MetricsOut MfaPolicy: properties: require_totp: type: boolean title: Require Totp require_for_roles: items: type: string type: array title: Require For Roles type: object required: - require_totp title: MfaPolicy MfaStatus: properties: enrolled: type: boolean title: Enrolled tenant_requires_totp: type: boolean title: Tenant Requires Totp recovery_codes_remaining: type: integer title: Recovery Codes Remaining has_totp: type: boolean title: Has Totp default: false has_webauthn: type: boolean title: Has Webauthn default: false require_for_roles: items: type: string type: array title: Require For Roles required_for_me: type: boolean title: Required For Me default: false type: object required: - enrolled - tenant_requires_totp - recovery_codes_remaining title: MfaStatus MiniTimelineEvent: properties: id: type: string title: Id ts: type: string format: date-time title: Ts kind: type: string title: Kind description: audit | case_comment | status_change | investigation | … agent: type: string title: Agent description: actor — system | analyst@… | agent-id summary: type: string title: Summary description: One-line description, plain text payload: anyOf: - additionalProperties: true type: object - type: 'null' title: Payload duration_ms: type: integer title: Duration Ms default: 0 type: object required: - id - ts - kind - agent - summary title: MiniTimelineEvent description: 'One event in the alert''s condensed timeline. Modelled after ``LedgerEvent`` in the ledger UI so the rail can reuse the same row renderer. Fields the rail doesn''t need (``input_hash``/``output_hash``/``duration_ms``) are always ``None``/``0`` for non-investigation events. The frontend knows to hide those affordances when they''re empty.' MispDryRunRequest: properties: indicator: anyOf: - $ref: '#/components/schemas/STIXIndicatorCreate' - type: 'null' bundle: anyOf: - $ref: '#/components/schemas/STIXBundleCreate' - type: 'null' distribution: anyOf: - type: integer maximum: 4.0 minimum: 0.0 - type: 'null' title: Distribution threat_level: anyOf: - type: integer maximum: 4.0 minimum: 1.0 - type: 'null' title: Threat Level analysis: anyOf: - type: integer maximum: 2.0 minimum: 0.0 - type: 'null' title: Analysis type: object title: MispDryRunRequest description: Either ``indicator`` or ``bundle`` must be provided. MispDryRunResponse: properties: event: additionalProperties: true type: object title: Event attribute_count: type: integer title: Attribute Count skipped_count: type: integer title: Skipped Count would_push_to: anyOf: - type: string - type: 'null' title: Would Push To airgap_blocked: type: boolean title: Airgap Blocked default: false airgap_message: anyOf: - type: string - type: 'null' title: Airgap Message type: object required: - event - attribute_count - skipped_count title: MispDryRunResponse MispPushHealth: properties: configured: type: boolean title: Configured airgapped: type: boolean title: Airgapped auto_push: type: boolean title: Auto Push url: anyOf: - type: string - type: 'null' title: Url user: anyOf: - type: string - type: 'null' title: User role: anyOf: - type: string - type: 'null' title: Role ok: type: boolean title: Ok error: anyOf: - type: string - type: 'null' title: Error type: object required: - configured - airgapped - auto_push - ok title: MispPushHealth MispPushResult: properties: pushed: type: boolean title: Pushed misp_event_id: anyOf: - type: string - type: 'null' title: Misp Event Id misp_event_uuid: anyOf: - type: string - type: 'null' title: Misp Event Uuid url: anyOf: - type: string - type: 'null' title: Url pushed_attributes: anyOf: - type: integer - type: 'null' title: Pushed Attributes skipped_attributes: anyOf: - type: integer - type: 'null' title: Skipped Attributes error: anyOf: - type: string - type: 'null' title: Error type: object required: - pushed title: MispPushResult description: Embedded in a STIX response when ``push_to_misp=true``. MitreCoverage: properties: covered: type: integer title: Covered total: type: integer title: Total ratio: type: number title: Ratio type: object required: - covered - total - ratio title: MitreCoverage description: "Slice of the MITRE ATT&CK landscape we have observable coverage\ \ for.\n\n``covered`` counts the distinct techniques observed via either:\n\ \ * a tenant alert in the selected window (``Alert.mitre_techniques``), or\n\ \ * an enabled tenant/platform detection rule\n (``DetectionRule.mitre_techniques``\ \ where ``status == 'enabled'``).\n\n``total`` is configurable via ``AISOC_FUNNEL_MITRE_TOTAL``\ \ and defaults to\nthe MITRE ATT&CK Enterprise v17 technique count (201) so\ \ the ratio is\ninterpretable as \"% of ATT&CK we're watching for\". The ratio\ \ is clamped to\n``[0.0, 1.0]`` even if operators downsize the universe." MitreCoverageCell: properties: techniqueId: type: string title: Techniqueid techniqueName: type: string title: Techniquename tactic: type: string title: Tactic detections: type: integer title: Detections alerts: type: integer title: Alerts intensity: type: number title: Intensity type: object required: - techniqueId - techniqueName - tactic - detections - alerts - intensity title: MitreCoverageCell MitreCoverageItem: properties: technique_id: type: string title: Technique Id name: anyOf: - type: string - type: 'null' title: Name tactic: anyOf: - type: string - type: 'null' title: Tactic alert_count: type: integer title: Alert Count type: object required: - technique_id - name - tactic - alert_count title: MitreCoverageItem MitreCoverageResponse: properties: tactics: items: type: string type: array title: Tactics cells: items: $ref: '#/components/schemas/MitreCoverageCell' type: array title: Cells generatedAt: type: string title: Generatedat type: object required: - tactics - cells - generatedAt title: MitreCoverageResponse MitreTactic: properties: tactic: type: string title: Tactic count: type: integer title: Count type: object required: - tactic - count title: MitreTactic ModelBreakdown: properties: model: type: string title: Model resolved_model: anyOf: - type: string - type: 'null' title: Resolved Model runs: type: integer title: Runs calls: type: integer title: Calls total_prompt_tokens: type: integer title: Total Prompt Tokens total_completion_tokens: type: integer title: Total Completion Tokens total_cost_usd: type: number title: Total Cost Usd measured_call_count: type: integer title: Measured Call Count default: 0 estimated_cost_usd: type: number title: Estimated Cost Usd default: 0.0 estimated_call_count: type: integer title: Estimated Call Count default: 0 unpriced_call_count: type: integer title: Unpriced Call Count default: 0 imputed_public_cost_usd: type: number title: Imputed Public Cost Usd imputed_is_estimable: type: boolean title: Imputed Is Estimable default: false unpriced_tokens: type: integer title: Unpriced Tokens default: 0 avg_latency_ms: anyOf: - type: number - type: 'null' title: Avg Latency Ms type: object required: - model - runs - calls - total_prompt_tokens - total_completion_tokens - total_cost_usd - imputed_public_cost_usd - avg_latency_ms title: ModelBreakdown description: Spend / volume rolled up per model over the window. ModelCostBreakdown: properties: model: type: string title: Model resolved_model: anyOf: - type: string - type: 'null' title: Resolved Model total_prompt_tokens: type: integer title: Total Prompt Tokens total_completion_tokens: type: integer title: Total Completion Tokens total_cost_usd: type: number title: Total Cost Usd measured_call_count: type: integer title: Measured Call Count default: 0 estimated_cost_usd: type: number title: Estimated Cost Usd default: 0.0 estimated_call_count: type: integer title: Estimated Call Count default: 0 unpriced_call_count: type: integer title: Unpriced Call Count default: 0 total_latency_ms: type: integer title: Total Latency Ms call_count: type: integer title: Call Count type: object required: - model - total_prompt_tokens - total_completion_tokens - total_cost_usd - total_latency_ms - call_count title: ModelCostBreakdown description: 'Per-model cost telemetry for a single run. Sourced from ``aisoc_run_costs`` (populated by ``CostTracker`` in the agents service). Lets operators audit which model drove spend on a given investigation.' NLDetectionRequest: properties: description: type: string maxLength: 2000 minLength: 10 title: Description description: Plain-English description of the threat behaviour to detect. target_platforms: items: type: string enum: - sigma - kql - spl - esql type: array title: Target Platforms description: Which rule languages to generate. default: - sigma - kql - spl - esql severity: type: string enum: - informational - low - medium - high - critical title: Severity default: medium mitre_techniques: items: type: string type: array title: Mitre Techniques type: object required: - description title: NLDetectionRequest NLDetectionResponse: properties: description: type: string title: Description sigma: anyOf: - type: string - type: 'null' title: Sigma kql: anyOf: - type: string - type: 'null' title: Kql spl: anyOf: - type: string - type: 'null' title: Spl esql: anyOf: - type: string - type: 'null' title: Esql model_used: type: string title: Model Used type: object required: - description - model_used title: NLDetectionResponse NLProposeRequest: properties: description: type: string maxLength: 2000 minLength: 10 title: Description severity: type: string enum: - informational - low - medium - high - critical title: Severity default: medium mitre_techniques: items: type: string type: array title: Mitre Techniques type: object required: - description title: NLProposeRequest NLProposeResponse: properties: proposal_id: anyOf: - type: string format: uuid - type: 'null' title: Proposal Id status: type: string title: Status passed: type: boolean title: Passed reason: type: string title: Reason sigma: type: string title: Sigma fixtures: additionalProperties: true type: object title: Fixtures model_used: type: string title: Model Used type: object required: - proposal_id - status - passed - reason - sigma - fixtures - model_used title: NLProposeResponse NLQueryExecuteRequest: properties: question: type: string minLength: 10 title: Question description: Plain-English security question (e.g. 'Show failed logins per user in the last 24 h'). index_pattern: type: string title: Index Pattern description: Elasticsearch index pattern to scope the ES|QL query. default: logs-*,aisoc-events-* time_range_hours: type: integer maximum: 8760.0 minimum: 1.0 title: Time Range Hours description: Look-back window in hours. default: 24 es_url: anyOf: - type: string - type: 'null' title: Es Url description: Select which of your tenant's Elasticsearch connectors to run against, by host. Matched against connectors you own; it is never used as an outbound target. Omit it to use your first enabled Elasticsearch connector. es_api_key: anyOf: - type: string - type: 'null' title: Es Api Key description: Rejected with 400. Credentials are read from the Elasticsearch connector saved for your tenant, where they are encrypted at rest. This field was previously documented as an override and silently ignored. max_rows: type: integer maximum: 5000.0 minimum: 1.0 title: Max Rows default: 500 type: object required: - question title: NLQueryExecuteRequest NLQueryExecuteResponse: properties: request_id: type: string format: uuid title: Request Id question: type: string title: Question esql: type: string title: Esql spl: type: string title: Spl kql: type: string title: Kql explanation: type: string title: Explanation created_at: type: string format: date-time title: Created At engine: type: string title: Engine description: '`deterministic` or `llm`.' default: deterministic grammar_validated: type: boolean title: Grammar Validated description: True if every emitted query passed grammar checks. default: true result: anyOf: - $ref: '#/components/schemas/QueryResult' - type: 'null' execution_error: anyOf: - type: string - type: 'null' title: Execution Error executed_against: anyOf: - type: string enum: - elasticsearch - lake - type: 'null' title: Executed Against unsupported_filters: items: type: string type: array title: Unsupported Filters type: object required: - request_id - question - esql - spl - kql - explanation - created_at title: NLQueryExecuteResponse NLQueryTranslateRequest: properties: question: type: string minLength: 10 title: Question description: Plain-English security question (e.g. 'Show failed logins per user in the last 24 h'). index_pattern: type: string title: Index Pattern description: Elasticsearch index pattern to scope the ES|QL query. default: logs-*,aisoc-events-* time_range_hours: type: integer maximum: 8760.0 minimum: 1.0 title: Time Range Hours description: Look-back window in hours. default: 24 type: object required: - question title: NLQueryTranslateRequest NLQueryTranslateResponse: properties: request_id: type: string format: uuid title: Request Id question: type: string title: Question esql: type: string title: Esql spl: type: string title: Spl kql: type: string title: Kql explanation: type: string title: Explanation created_at: type: string format: date-time title: Created At engine: type: string title: Engine description: '`deterministic` or `llm`.' default: deterministic grammar_validated: type: boolean title: Grammar Validated description: True if every emitted query passed grammar checks. default: true type: object required: - request_id - question - esql - spl - kql - explanation - created_at title: NLQueryTranslateResponse NodeCreate: properties: node_type: type: string title: Node Type external_id: type: string title: External Id display_name: anyOf: - type: string - type: 'null' title: Display Name source_system: type: string title: Source System risk_score: type: number title: Risk Score default: 0.0 privilege_tier: type: integer title: Privilege Tier default: 0 last_activity: anyOf: - type: string format: date-time - type: 'null' title: Last Activity attributes: additionalProperties: true type: object title: Attributes type: object required: - node_type - external_id - source_system title: NodeCreate NodeOut: properties: node_type: type: string title: Node Type external_id: type: string title: External Id display_name: anyOf: - type: string - type: 'null' title: Display Name source_system: type: string title: Source System risk_score: type: number title: Risk Score default: 0.0 privilege_tier: type: integer title: Privilege Tier default: 0 last_activity: anyOf: - type: string format: date-time - type: 'null' title: Last Activity attributes: additionalProperties: true type: object title: Attributes id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id is_active: type: boolean title: Is Active created_at: type: string title: Created At updated_at: type: string title: Updated At type: object required: - node_type - external_id - source_system - id - tenant_id - is_active - created_at - updated_at title: NodeOut OAuthAppRegistration: properties: client_id: type: string maxLength: 512 minLength: 1 title: Client Id client_secret: type: string maxLength: 4096 minLength: 1 title: Client Secret authorize_url: anyOf: - type: string maxLength: 512 - type: 'null' title: Authorize Url token_url: anyOf: - type: string maxLength: 512 - type: 'null' title: Token Url scopes: anyOf: - items: type: string type: array maxItems: 64 - type: 'null' title: Scopes type: object required: - client_id - client_secret title: OAuthAppRegistration description: Payload for ``PUT /api/v1/oauth/app/{connector_type}``. OAuthAppView: properties: connector_type: type: string title: Connector Type client_id: type: string title: Client Id has_secret: type: boolean title: Has Secret authorize_url: anyOf: - type: string - type: 'null' title: Authorize Url token_url: anyOf: - type: string - type: 'null' title: Token Url scopes: anyOf: - items: type: string type: array - type: 'null' title: Scopes created_at: type: string format: date-time title: Created At updated_at: type: string format: date-time title: Updated At type: object required: - connector_type - client_id - has_secret - authorize_url - token_url - scopes - created_at - updated_at title: OAuthAppView description: 'Read-only projection of an :class:`OAuthAppCredential`. Never round-trips the secret — only ``has_secret: bool`` so the UI can render a "credential present, last updated X days ago" badge.' ObservableEdge: properties: source: type: string title: Source target: type: string title: Target relation: type: string title: Relation type: object required: - source - target - relation title: ObservableEdge ObservableNode: properties: id: type: string title: Id kind: type: string enum: - ip - user - host - domain - hash - file - process - alert title: Kind value: type: string title: Value tags: items: type: string type: array title: Tags type: object required: - id - kind - value title: ObservableNode OnCallEntry: properties: user_id: type: string format: uuid title: User Id user_email: anyOf: - type: string - type: 'null' title: User Email user_name: anyOf: - type: string - type: 'null' title: User Name status: type: string title: Status schedule_ref: anyOf: - type: string - type: 'null' title: Schedule Ref rotation: anyOf: - type: string - type: 'null' title: Rotation note: anyOf: - type: string - type: 'null' title: Note until: anyOf: - type: string format: date-time - type: 'null' title: Until updated_at: type: string format: date-time title: Updated At type: object required: - user_id - status - updated_at title: OnCallEntry OnCallListResponse: properties: items: items: $ref: '#/components/schemas/OnCallEntry' type: array title: Items total: type: integer title: Total type: object required: - items - total title: OnCallListResponse OnCallUpdateRequest: properties: status: type: string enum: - available - busy - offline - snoozed title: Status note: anyOf: - type: string maxLength: 500 - type: 'null' title: Note snooze_minutes: anyOf: - type: integer maximum: 1440.0 minimum: 1.0 - type: 'null' title: Snooze Minutes rotation: anyOf: - type: string maxLength: 80 - type: 'null' title: Rotation schedule_ref: anyOf: - type: string maxLength: 200 - type: 'null' title: Schedule Ref type: object required: - status title: OnCallUpdateRequest OnboardingStatus: properties: first_run: type: boolean title: First Run description: True when this tenant has no connectors and no alerts. The console routes a first-run tenant to the setup wizard rather than to an empty dashboard. steps: items: $ref: '#/components/schemas/SetupStep' type: array title: Steps connectors: type: integer title: Connectors alerts: type: integer title: Alerts sample_data_loaded: type: boolean title: Sample Data Loaded type: object required: - first_run - steps - connectors - alerts - sample_data_loaded title: OnboardingStatus OpenWarRoomRequest: properties: provider: type: string title: Provider description: slack | teams default: slack invite: items: type: string type: array maxItems: 100 title: Invite type: object title: OpenWarRoomRequest OrganizationCreate: properties: slug: type: string maxLength: 64 minLength: 2 pattern: ^[a-z0-9][a-z0-9-]*$ title: Slug name: type: string maxLength: 255 minLength: 1 title: Name kind: type: string pattern: ^(mssp|enterprise)$ title: Kind default: mssp type: object required: - slug - name title: OrganizationCreate OrganizationOut: properties: id: type: string format: uuid title: Id slug: type: string title: Slug name: type: string title: Name kind: type: string title: Kind home_tenant_id: anyOf: - type: string format: uuid - type: 'null' title: Home Tenant Id is_active: type: boolean title: Is Active type: object required: - id - slug - name - kind - home_tenant_id - is_active title: OrganizationOut OverrideEntryModel: properties: key: type: string title: Key tags: items: type: string type: array title: Tags reason: anyOf: - type: string - type: 'null' title: Reason created_at: anyOf: - type: string - type: 'null' title: Created At value: additionalProperties: true type: object title: Value type: object required: - key - tags - reason - created_at - value title: OverrideEntryModel OverrideSummaryResponse: properties: total_overrides: type: integer title: Total Overrides false_positive_corrections: type: integer title: False Positive Corrections true_positive_corrections: type: integer title: True Positive Corrections benign_true_positive_corrections: type: integer title: Benign True Positive Corrections default: 0 benign_corrections: type: integer title: Benign Corrections escalate_corrections: type: integer title: Escalate Corrections type: object required: - total_overrides - false_positive_corrections - true_positive_corrections - benign_corrections - escalate_corrections title: OverrideSummaryResponse OverviewEdge: properties: id: type: string title: Id source: type: string title: Source target: type: string title: Target label: type: string title: Label attributes: additionalProperties: true type: object title: Attributes type: object required: - id - source - target - label title: OverviewEdge OverviewNode: properties: id: type: string title: Id label: type: string title: Label kind: type: string title: Kind riskScore: anyOf: - type: number - type: 'null' title: Riskscore severity: anyOf: - type: string - type: 'null' title: Severity attributes: additionalProperties: true type: object title: Attributes type: object required: - id - label - kind title: OverviewNode PackAssignmentCreate: properties: child_tenant_id: type: string format: uuid title: Child Tenant Id enabled: type: boolean title: Enabled default: true parameter_overrides: additionalProperties: true type: object title: Parameter Overrides default: {} type: object required: - child_tenant_id title: PackAssignmentCreate PackAssignmentOut: properties: id: type: string format: uuid title: Id pack_id: type: string format: uuid title: Pack Id child_tenant_id: type: string format: uuid title: Child Tenant Id enabled: type: boolean title: Enabled parameter_overrides: additionalProperties: true type: object title: Parameter Overrides created_at: type: string title: Created At type: object required: - id - pack_id - child_tenant_id - enabled - parameter_overrides - created_at title: PackAssignmentOut ParserIn: properties: name: type: string maxLength: 200 minLength: 1 title: Name source: type: string title: Source default: generic transforms: items: additionalProperties: true type: object type: array title: Transforms enabled: type: boolean title: Enabled default: true type: object required: - name title: ParserIn ParserOut: properties: id: type: string format: uuid title: Id name: type: string title: Name source: type: string title: Source transforms: items: additionalProperties: true type: object type: array title: Transforms enabled: type: boolean title: Enabled type: object required: - id - name - source - transforms - enabled title: ParserOut ParserTestIn: properties: transforms: items: additionalProperties: true type: object type: array title: Transforms sample_event: additionalProperties: true type: object title: Sample Event type: object required: - transforms - sample_event title: ParserTestIn ParserTestOut: properties: event: additionalProperties: true type: object title: Event warnings: items: type: string type: array title: Warnings type: object required: - event - warnings title: ParserTestOut PasskeyCredentialOut: properties: id: type: string format: uuid title: Id device_name: type: string title: Device Name transports: items: type: string type: array title: Transports is_discoverable: type: boolean title: Is Discoverable last_used_at: anyOf: - type: string format: date-time - type: 'null' title: Last Used At created_at: type: string format: date-time title: Created At type: object required: - id - device_name - transports - is_discoverable - last_used_at - created_at title: PasskeyCredentialOut PeerGroupCreate: properties: name: type: string title: Name description: anyOf: - type: string - type: 'null' title: Description criteria: additionalProperties: true type: object title: Criteria type: object required: - name title: PeerGroupCreate PeerGroupOut: properties: name: type: string title: Name description: anyOf: - type: string - type: 'null' title: Description criteria: additionalProperties: true type: object title: Criteria id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id created_at: type: string title: Created At type: object required: - name - id - tenant_id - created_at title: PeerGroupOut PermissionNameIn: properties: permission: type: string maxLength: 200 minLength: 1 title: Permission type: object required: - permission title: PermissionNameIn PermissionNamesIn: properties: permissions: items: type: string type: array minItems: 0 title: Permissions type: object required: - permissions title: PermissionNamesIn description: 'Body for `PUT /roles/{id}/permissions`. The console sends `{"permissions": ["alerts:read", ...]}` and the route additionally accepts the bare list shape (see the endpoint); either way every key is validated against the platform vocabulary before anything is deleted, so an unknown key is a 400 that names the valid set instead of a silent partial update.' PermissionOut: properties: id: type: string format: uuid title: Id name: type: string title: Name description: anyOf: - type: string - type: 'null' title: Description category: anyOf: - type: string - type: 'null' title: Category type: object required: - id - name - description - category title: PermissionOut PipelineHealth: properties: overall_status: type: string title: Overall Status stages: items: $ref: '#/components/schemas/PipelineStage' type: array title: Stages generated_at: type: string format: date-time title: Generated At type: object required: - overall_status - stages - generated_at title: PipelineHealth description: Top-level response for ``GET /health/pipeline``. PipelineStage: properties: stage: type: string title: Stage backlog: type: integer title: Backlog p95_latency_ms: type: number title: P95 Latency Ms error_rate: type: number title: Error Rate status: type: string title: Status unmeasured: items: type: string type: array title: Unmeasured type: object required: - stage - backlog - p95_latency_ms - error_rate - status title: PipelineStage description: 'One row in the pipeline health strip. ``status`` follows the green/yellow/red/unknown ladder used by ``app.services.connector_freshness``. ``backlog`` is the number of items currently waiting at this stage (e.g. enabled-but-stale connectors for ``ingest``, unacked alerts for ``alert``). ``p95_latency_ms`` is the 95th percentile end-to-end latency for items that traversed this stage in the last hour. ``error_rate`` is errors / processed in the same window.' PluginManifestOut: properties: id: type: string title: Id name: type: string title: Name version: type: string title: Version plugin_type: type: string title: Plugin Type description: type: string title: Description author: type: string title: Author tags: items: type: string type: array title: Tags config_schema: additionalProperties: true type: object title: Config Schema type: object required: - id - name - version - plugin_type - description - author - tags - config_schema title: PluginManifestOut PluginOut: properties: id: type: string title: Id manifest: $ref: '#/components/schemas/PluginManifestOut' enabled: type: boolean title: Enabled loaded_at: type: number title: Loaded At error: anyOf: - type: string - type: 'null' title: Error type: object required: - id - manifest - enabled - loaded_at - error title: PluginOut PortfolioAlertOut: properties: alert_id: type: string format: uuid title: Alert Id tenant_id: type: string format: uuid title: Tenant Id tenant_name: type: string title: Tenant Name title: type: string title: Title severity: type: string title: Severity status: type: string title: Status category: anyOf: - type: string - type: 'null' title: Category created_at: anyOf: - type: string - type: 'null' title: Created At event_time: anyOf: - type: string - type: 'null' title: Event Time case_id: anyOf: - type: string format: uuid - type: 'null' title: Case Id is_synthetic: type: boolean title: Is Synthetic type: object required: - alert_id - tenant_id - tenant_name - title - severity - status - category - created_at - event_time - case_id - is_synthetic title: PortfolioAlertOut PortfolioOut: properties: org_id: anyOf: - type: string format: uuid - type: 'null' title: Org Id org_slug: anyOf: - type: string - type: 'null' title: Org Slug org_name: anyOf: - type: string - type: 'null' title: Org Name org_role: anyOf: - type: string - type: 'null' title: Org Role portfolio_wide: type: boolean title: Portfolio Wide description: True when the caller's role reaches the whole portfolio rather than a set of explicit tenant grants. scoped_tenants: type: integer title: Scoped Tenants summary: $ref: '#/components/schemas/PortfolioSummaryOut' tenants: items: $ref: '#/components/schemas/PortfolioTenantOut' type: array title: Tenants type: object required: - org_id - org_slug - org_name - org_role - portfolio_wide - scoped_tenants - summary - tenants title: PortfolioOut PortfolioSummaryOut: properties: tenants: type: integer title: Tenants tenants_active: type: integer title: Tenants Active open_alerts: type: integer title: Open Alerts critical_alerts: type: integer title: Critical Alerts high_alerts: type: integer title: High Alerts untriaged_alerts: type: integer title: Untriaged Alerts synthetic_alerts: type: integer title: Synthetic Alerts open_cases: type: integer title: Open Cases sla_breached_cases: type: integer title: Sla Breached Cases mttr_minutes: anyOf: - type: number - type: 'null' title: Mttr Minutes description: Mean of the per-tenant figures, over tenants that closed something. Null when the portfolio closed nothing. connectors_total: type: integer title: Connectors Total connectors_healthy: type: integer title: Connectors Healthy connectors_stale: type: integer title: Connectors Stale connectors_error: type: integer title: Connectors Error tenants_with_exhausted_limits: type: integer title: Tenants With Exhausted Limits tenants_with_limit_warnings: type: integer title: Tenants With Limit Warnings tenants_without_connectors: type: integer title: Tenants Without Connectors type: object required: - tenants - tenants_active - open_alerts - critical_alerts - high_alerts - untriaged_alerts - synthetic_alerts - open_cases - sla_breached_cases - mttr_minutes - connectors_total - connectors_healthy - connectors_stale - connectors_error - tenants_with_exhausted_limits - tenants_with_limit_warnings - tenants_without_connectors title: PortfolioSummaryOut PortfolioTenantOut: properties: tenant_id: type: string format: uuid title: Tenant Id name: type: string title: Name slug: type: string title: Slug relationship: type: string title: Relationship is_active: type: boolean title: Is Active open_alerts: type: integer title: Open Alerts critical_alerts: type: integer title: Critical Alerts high_alerts: type: integer title: High Alerts untriaged_alerts: type: integer title: Untriaged Alerts synthetic_alerts: type: integer title: Synthetic Alerts description: Seeded demo rows, counted apart from the figures above. open_cases: type: integer title: Open Cases sla_breached_cases: type: integer title: Sla Breached Cases mttr_minutes: anyOf: - type: number - type: 'null' title: Mttr Minutes description: Mean minutes to close, over cases this tenant closed in the trailing 30 days. Null when it closed none. connectors: $ref: '#/components/schemas/ConnectorHealthOut' last_event_at: anyOf: - type: string - type: 'null' title: Last Event At limits: items: $ref: '#/components/schemas/LimitHeadroomOut' type: array title: Limits limits_exhausted: type: integer title: Limits Exhausted limits_warning: type: integer title: Limits Warning type: object required: - tenant_id - name - slug - relationship - is_active - open_alerts - critical_alerts - high_alerts - untriaged_alerts - synthetic_alerts - open_cases - sla_breached_cases - mttr_minutes - connectors - last_event_at - limits - limits_exhausted - limits_warning title: PortfolioTenantOut PostureSummary: properties: total: type: integer title: Total critical: type: integer title: Critical high: type: integer title: High medium: type: integer title: Medium low: type: integer title: Low open: type: integer title: Open resolved: type: integer title: Resolved suppressed: type: integer title: Suppressed type: object required: - total - critical - high - medium - low - open - resolved - suppressed title: PostureSummary PreferencesPatch: properties: preferences: additionalProperties: true type: object title: Preferences type: object required: - preferences title: PreferencesPatch description: Partial update payload for user preferences (merged server-side). PreviewRequest: properties: yaml: type: string maxLength: 262144 title: Yaml alerts: items: additionalProperties: true type: object type: array title: Alerts type: object required: - yaml title: PreviewRequest PreviewResponse: properties: sample_size: type: integer title: Sample Size changed_count: type: integer title: Changed Count suppressed_count: type: integer title: Suppressed Count elapsed_ms: type: number title: Elapsed Ms rows: items: $ref: '#/components/schemas/PreviewRow' type: array title: Rows type: object required: - sample_size - changed_count - suppressed_count - elapsed_ms - rows title: PreviewResponse PreviewRow: properties: alert_id: type: string title: Alert Id matched_rule_ids: items: type: string type: array title: Matched Rule Ids before: additionalProperties: true type: object title: Before after: additionalProperties: true type: object title: After suppressed: type: boolean title: Suppressed changed: type: boolean title: Changed type: object required: - alert_id - matched_rule_ids - before - after - suppressed - changed title: PreviewRow PrimaryRoleAssignment: properties: role_name: type: string maxLength: 100 minLength: 1 title: Role Name reason: anyOf: - type: string maxLength: 500 - type: 'null' title: Reason type: object required: - role_name title: PrimaryRoleAssignment description: 'Body for `PUT /rbac/users/{id}/role` — replaces the primary role. `role_name` names a seeded catalog role (`viewer` | `infosec` | `admin` or a tenant custom role); the server resolves it against this tenant''s catalog and refuses anything it does not recognize with a 400 naming the valid set — a 500 would leak the schema, and a silent 404 would make a typo look like an outage.' PromotionRequest: properties: scope_kind: type: string title: Scope Kind description: alert_class or action_verb scope_key: type: string maxLength: 200 minLength: 1 title: Scope Key capability: type: string title: Capability description: auto_close or auto_execute override: type: boolean title: Override default: false override_reason: anyOf: - type: string maxLength: 500 - type: 'null' title: Override Reason type: object required: - scope_kind - scope_key - capability title: PromotionRequest PromotionResponse: properties: granted: type: boolean title: Granted state: type: string title: State source: type: string title: Source is_override: type: boolean title: Is Override refusals: items: type: string type: array title: Refusals evidence: additionalProperties: true type: object title: Evidence changed: type: boolean title: Changed type: object required: - granted - state - source - is_override - refusals - evidence - changed title: PromotionResponse ProposalResponse: properties: positive_fixtures: items: additionalProperties: true type: object type: array title: Positive Fixtures negative_fixtures: items: additionalProperties: true type: object type: array title: Negative Fixtures id: type: string format: uuid title: Id tenant_id: anyOf: - type: string format: uuid - type: 'null' title: Tenant Id base_rule_id: anyOf: - type: string format: uuid - type: 'null' title: Base Rule Id promoted_rule_id: anyOf: - type: string format: uuid - type: 'null' title: Promoted Rule Id name: type: string title: Name description: anyOf: - type: string - type: 'null' title: Description rule_language: type: string title: Rule Language rule_body: type: string title: Rule Body category: type: string title: Category severity: type: string title: Severity confidence: type: integer title: Confidence mitre_tactics: items: {} type: array title: Mitre Tactics mitre_techniques: items: {} type: array title: Mitre Techniques tags: items: {} type: array title: Tags status: type: string title: Status eval_result: additionalProperties: true type: object title: Eval Result review_comments: items: {} type: array title: Review Comments proposed_by_id: anyOf: - type: string format: uuid - type: 'null' title: Proposed By Id decided_by_id: anyOf: - type: string format: uuid - type: 'null' title: Decided By Id decision_comment: anyOf: - type: string - type: 'null' title: Decision Comment decided_at: anyOf: - type: string format: date-time - type: 'null' title: Decided At github_pr_url: anyOf: - type: string - type: 'null' title: Github Pr Url created_at: type: string format: date-time title: Created At updated_at: type: string format: date-time title: Updated At type: object required: - id - tenant_id - base_rule_id - promoted_rule_id - name - description - rule_language - rule_body - category - severity - confidence - mitre_tactics - mitre_techniques - tags - status - eval_result - review_comments - proposed_by_id - decided_by_id - decision_comment - decided_at - created_at - updated_at title: ProposalResponse ProviderInfo: properties: name: type: string title: Name local: type: boolean title: Local usable: type: boolean title: Usable supports_file_submission: type: boolean title: Supports File Submission supports_url_submission: type: boolean title: Supports Url Submission submissions_are_public_by_default: type: boolean title: Submissions Are Public By Default description: type: string title: Description uploads_enabled_for_tenant: type: boolean title: Uploads Enabled For Tenant consent_text: type: string title: Consent Text consent_text_version: type: string title: Consent Text Version authentication_verified: type: boolean title: Authentication Verified unusable_reason: anyOf: - type: string - type: 'null' title: Unusable Reason type: object required: - name - local - usable - supports_file_submission - supports_url_submission - submissions_are_public_by_default - description - uploads_enabled_for_tenant - consent_text - consent_text_version - authentication_verified title: ProviderInfo ProvidersResponse: properties: airgapped: type: boolean title: Airgapped providers: items: $ref: '#/components/schemas/ProviderInfo' type: array title: Providers type: object required: - airgapped - providers title: ProvidersResponse PublicReplay: properties: slug: type: string title: Slug title: type: string title: Title case_id: type: string title: Case Id snapshot: additionalProperties: true type: object title: Snapshot view_count: type: integer title: View Count created_at: type: string format: date-time title: Created At type: object required: - slug - title - case_id - snapshot - view_count - created_at title: PublicReplay PublishPreview: properties: run_id: type: string format: uuid title: Run Id title: type: string title: Title snapshot: additionalProperties: true type: object title: Snapshot alias_map: additionalProperties: type: string type: object title: Alias Map type: object required: - run_id - title - snapshot - alias_map title: PublishPreview description: 'Pre-publish diff: what the public will see + what will be hidden.' PublishRequest: properties: confirm: type: boolean title: Confirm description: Must be true to publish. default: false title_override: anyOf: - type: string maxLength: 200 - type: 'null' title: Title Override type: object title: PublishRequest PublishResult: properties: slug: type: string title: Slug url: type: string title: Url run_id: type: string format: uuid title: Run Id created_at: type: string format: date-time title: Created At type: object required: - slug - url - run_id - created_at title: PublishResult PublishStatus: type: string enum: - pending - approved - rejected title: PublishStatus PublishedSummary: properties: slug: type: string title: Slug url: type: string title: Url title: type: string title: Title case_id: type: string title: Case Id view_count: type: integer title: View Count created_at: type: string format: date-time title: Created At type: object required: - slug - url - title - case_id - view_count - created_at title: PublishedSummary PublisherKeyIn: properties: public_key_pem: type: string title: Public Key Pem description: PEM-encoded Ed25519 public key. label: type: string maxLength: 120 title: Label description: Which machine or CI job holds the private half. default: '' display_name: type: string maxLength: 200 title: Display Name default: '' contact_email: type: string maxLength: 320 title: Contact Email default: '' type: object required: - public_key_pem title: PublisherKeyIn PublisherKeyOut: properties: fingerprint: type: string title: Fingerprint label: type: string title: Label publisher_id: type: string format: uuid title: Publisher Id type: object required: - fingerprint - label - publisher_id title: PublisherKeyOut PurgeRequest: properties: execute: type: boolean title: Execute description: 'False previews: every mailbox is graded and reported, nothing is removed, and no row reads ''purged''. Preview is the default because this is the most disruptive verb the product offers.' default: false confidence: anyOf: - type: number maximum: 1.0 minimum: 0.0 - type: 'null' title: Confidence type: object title: PurgeRequest QueryRequest: properties: question: type: string minLength: 3 title: Question doc_kinds: items: type: string enum: - runbook - policy - playbook - sop - wiki - other type: array title: Doc Kinds top_k: type: integer maximum: 20.0 minimum: 1.0 title: Top K default: 5 synthesise: type: boolean title: Synthesise description: Use LLM to synthesise an answer from retrieved chunks. default: true recall_memory: type: boolean title: Recall Memory description: Append semantically recalled memory items to the result. Never reorders the full-text chunks. Off by default; see scripts/score_memory_recall.py. default: false type: object required: - question title: QueryRequest QueryResponse: properties: question: type: string title: Question chunks: items: $ref: '#/components/schemas/KBChunk' type: array title: Chunks answer: anyOf: - type: string - type: 'null' title: Answer sources: items: type: string type: array title: Sources memory: items: $ref: '#/components/schemas/MemoryRecallChunk' type: array title: Memory memory_unavailable: anyOf: - type: string - type: 'null' title: Memory Unavailable type: object required: - question - chunks - answer - sources title: QueryResponse QueryResult: properties: columns: items: type: string type: array title: Columns rows: items: items: {} type: array type: array title: Rows total_rows: type: integer title: Total Rows took_ms: anyOf: - type: integer - type: 'null' title: Took Ms type: object required: - columns - rows - total_rows title: QueryResult QueueAction: properties: priority: type: integer title: Priority action: type: string title: Action risk: type: string title: Risk default: low type: object required: - priority - action title: QueueAction description: 'Top suggested next action for a queue row. Sourced from ``Alert.ai_recommendations``. The lowest-priority integer wins (1 = most urgent). String-only legacy values are accepted and mapped to ``risk="low"`` with the array index as a fallback priority.' QueueAsset: properties: kind: type: string title: Kind description: host | user | ip | asset value: type: string title: Value label: anyOf: - type: string - type: 'null' title: Label type: object required: - kind - value title: QueueAsset description: 'Lightweight asset descriptor for a queue row. The queue surfaces *one* representative entity per alert so the row is informative at a glance ("VPN brute force on `web-01`") without enumerating every entity. The full pivotable list lives on the Investigation Rail when the analyst opens the alert.' QueueItem: properties: id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id title: type: string title: Title severity: type: string title: Severity status: type: string title: Status priority: type: integer title: Priority category: anyOf: - type: string - type: 'null' title: Category connector_type: anyOf: - type: string - type: 'null' title: Connector Type assigned_to_id: anyOf: - type: string format: uuid - type: 'null' title: Assigned To Id case_id: anyOf: - type: string format: uuid - type: 'null' title: Case Id first_seen: type: string format: date-time title: First Seen sla_due_at: type: string format: date-time title: Sla Due At sla_remaining_seconds: type: integer title: Sla Remaining Seconds sla_breached: type: boolean title: Sla Breached age_seconds: type: integer title: Age Seconds asset: anyOf: - $ref: '#/components/schemas/QueueAsset' - type: 'null' suggested_action: anyOf: - $ref: '#/components/schemas/QueueAction' - type: 'null' bucket: type: string enum: - mine - unassigned title: Bucket priority_score: type: integer title: Priority Score default: 0 priority_basis: additionalProperties: true type: object title: Priority Basis type: object required: - id - tenant_id - title - severity - status - priority - first_seen - sla_due_at - sla_remaining_seconds - sla_breached - age_seconds - bucket title: QueueItem description: 'One row in the Investigation Queue workbench. Intentionally slimmer than ``AlertResponse``. The queue exists to answer "what should I work on next?" — clicking a row navigates to ``/alerts/{id}`` which loads full detail via the existing detail endpoint (``GET /alerts/{id}``). Keeping this payload small means a busy SOC with thousands of open alerts can refresh the queue cheaply (the badge on the topbar polls it).' QueueResponse: properties: items: items: $ref: '#/components/schemas/QueueItem' type: array title: Items total: type: integer title: Total counts: additionalProperties: type: integer type: object title: Counts period: type: string title: Period owner: type: string title: Owner page: type: integer title: Page page_size: type: integer title: Page Size pages: type: integer title: Pages generated_at: type: string format: date-time title: Generated At type: object required: - items - total - counts - period - owner - page - page_size - pages - generated_at title: QueueResponse description: 'Paginated queue response. ``counts`` always reports both buckets so the workbench can render the "mine / unassigned / all" tabs without re-fetching. ``generated_at`` is the server-side ``now`` we used to compute SLA remaining — the frontend can drift its own clock from this so multiple analysts on different boxes see the same countdowns.' RatingIn: properties: score: type: integer maximum: 5.0 minimum: 1.0 title: Score comment: anyOf: - type: string - type: 'null' title: Comment type: object required: - score title: RatingIn RealtimeTicket: properties: token: type: string title: Token description: Signed HS256 ticket for the WS/SSE edge. expires_in: type: integer title: Expires In description: Ticket lifetime in seconds. tenant_id: type: string title: Tenant Id description: Tenant the ticket is scoped to. type: object required: - token - expires_in - tenant_id title: RealtimeTicket description: Response body for ``POST /realtime/ticket``. RecommendedAction: properties: priority: type: integer minimum: 1.0 title: Priority default: 99 action: type: string title: Action rationale: anyOf: - type: string - type: 'null' title: Rationale risk: type: string title: Risk description: low | medium | high default: low type: object required: - action title: RecommendedAction description: 'Structured action emitted by the ResponderAgent at investigation close. Tolerates the legacy list-of-strings shape that older demo seeds used (string → ``action`` with priority ``99`` and risk ``"low"``). The plan calls for the structured shape; this is a migration aid.' RedispositionApplyRequest: properties: alert_ids: items: type: string type: array title: Alert Ids new_disposition: type: string title: New Disposition confirmation_token: type: string title: Confirmation Token description: Token from the /alert-override preview over this exact alert set (Phase 1.2 blast-radius control) type: object required: - alert_ids - new_disposition - confirmation_token title: RedispositionApplyRequest RedispositionApplyResponse: properties: updated: type: integer title: Updated new_disposition: type: string title: New Disposition type: object required: - updated - new_disposition title: RedispositionApplyResponse RedispositionCandidateModel: properties: alert_id: type: string title: Alert Id title: type: string title: Title severity: type: string title: Severity current_disposition: anyOf: - type: string - type: 'null' title: Current Disposition proposed_disposition: type: string title: Proposed Disposition event_time: type: string title: Event Time type: object required: - alert_id - title - severity - current_disposition - proposed_disposition - event_time title: RedispositionCandidateModel ReferenceLookupRequest: properties: names: items: type: string type: array maxItems: 32 title: Names tenant_id: anyOf: - type: string format: uuid - type: 'null' title: Tenant Id type: object title: ReferenceLookupRequest description: The ``${NAME}`` tokens one ``http`` step needs resolved. RefreshRequest: properties: refresh_token: type: string title: Refresh Token type: object required: - refresh_token title: RefreshRequest RegisterBeginRequest: properties: device_name: type: string maxLength: 120 title: Device Name default: My phone type: object title: RegisterBeginRequest RelatedEntity: properties: group: type: string title: Group description: 'Rail column: principal | network | workflow | tenant' kind: type: string title: Kind description: 'Concrete entity type: host | ip | user | rule | mitre | …' value: type: string title: Value description: Canonical identifier rendered in the chip label: anyOf: - type: string - type: 'null' title: Label description: Optional human-friendly label pivot: anyOf: - type: string - type: 'null' title: Pivot description: Frontend route to deep-link into AttackGraphView (or another workbench). When None the chip is informational only. type: object required: - group - kind - value title: RelatedEntity description: 'One pivotable entity attached to an alert. The ``kind`` field is the *concrete* entity type (``host``, ``ip``, ``user``, ``rule``, …) — the frontend keys icons and pivot routes off it. The ``group`` field is the rail column the entity renders under (``principal``, ``network``, ``workflow``, ``tenant``). One entity belongs to exactly one group; we don''t duplicate rows.' ReleaseRequest: properties: reason: type: string maxLength: 500 minLength: 8 title: Reason type: object required: - reason title: ReleaseRequest ReopenCaseRequest: properties: reason: type: string maxLength: 2000 minLength: 8 title: Reason description: Why the case is being reopened. Recorded on the case and in the audit log. status: type: string enum: - new - triaged - investigating title: Status default: investigating type: object required: - reason title: ReopenCaseRequest description: Why this case is coming back. ReplaceRulesRequest: properties: yaml: type: string maxLength: 262144 title: Yaml type: object required: - yaml title: ReplaceRulesRequest ReplayableConnectorInfo: properties: connector_type: type: string title: Connector Type vendor: type: string title: Vendor label: type: string title: Label type: object required: - connector_type - vendor - label title: ReplayableConnectorInfo ReportDefinitionBody: properties: name: type: string maxLength: 200 minLength: 1 title: Name widgets: items: additionalProperties: true type: object type: array title: Widgets type: object required: - name title: ReportDefinitionBody ReportRequest: properties: source: type: string enum: - m365_graph - google_workspace - email_inbox title: Source payload: additionalProperties: true type: object title: Payload description: 'The vendor''s own payload, unmodified: a Microsoft Graph message resource, a Gmail Message, or the event the email_inbox connector emits. Normalised server-side by app.services.phishing.intake, which is the only place that knows the three shapes.' reported_by: type: string title: Reported By description: Who reported it, when the payload does not say. default: '' type: object required: - source - payload title: ReportRequest ReportResponse: properties: submission: $ref: '#/components/schemas/SubmissionResponse' campaign_id: anyOf: - type: string format: uuid - type: 'null' title: Campaign Id campaign_created: type: boolean title: Campaign Created analysis_run_id: anyOf: - type: string format: uuid - type: 'null' title: Analysis Run Id auth_results: additionalProperties: type: string type: object title: Auth Results artefacts_analysed: type: integer title: Artefacts Analysed default: 0 artefacts_unanalysed: type: integer title: Artefacts Unanalysed default: 0 grouping_basis: type: string title: Grouping Basis default: '' grouping_is_factual: type: boolean title: Grouping Is Factual default: false recipient_count: type: integer title: Recipient Count default: 0 type: object required: - submission - campaign_id - campaign_created title: ReportResponse RepositoryScanRequest: properties: repositories: additionalProperties: additionalProperties: type: string type: object type: object title: Repositories description: 'repository -> {path: content}. Only recognised AI artefact paths are read.' type: object required: - repositories title: RepositoryScanRequest description: 'Files a code-platform connector has already fetched. The console passes what the connector read rather than this service cloning anything: the connector already holds the tenant''s code-platform credential and the governed read path, and a second fetcher here would be a second place that credential has to live.' ResetRequest: properties: reason: type: string maxLength: 500 minLength: 3 title: Reason type: object required: - reason title: ResetRequest ResolveRequest: properties: records: items: $ref: '#/components/schemas/IdentityRecord' type: array title: Records persist: type: boolean title: Persist default: false type: object title: ResolveRequest RetentionOut: properties: raw_events_days: type: integer title: Raw Events Days alerts_days: type: integer title: Alerts Days audit_days: type: integer title: Audit Days type: object required: - raw_events_days - alerts_days - audit_days title: RetentionOut RetentionUpdate: properties: raw_events_days: anyOf: - type: integer maximum: 3650.0 minimum: 1.0 - type: 'null' title: Raw Events Days alerts_days: anyOf: - type: integer maximum: 3650.0 minimum: 1.0 - type: 'null' title: Alerts Days audit_days: anyOf: - type: integer maximum: 3650.0 minimum: 1.0 - type: 'null' title: Audit Days type: object title: RetentionUpdate RetroHuntSettingsIn: properties: enabled: type: boolean title: Enabled description: Whether AiSOC may sweep this tenant's history at all. lookback_days: type: integer maximum: 365.0 minimum: 1.0 title: Lookback Days description: How far back a sweep looks. Bounded so no settings row can express 'scan everything'. default: 30 include_federated: type: boolean title: Include Federated description: 'Whether to go beyond AiSOC''s own lake into connected SIEMs. Separate from `enabled` because it is a separate cost: a lake sweep is free to you and a SIEM sweep may not be.' default: true type: object required: - enabled title: RetroHuntSettingsIn RetroHuntSettingsOut: properties: enabled: type: boolean title: Enabled lookback_days: type: integer title: Lookback Days include_federated: type: boolean title: Include Federated max_sweeps_per_hour: type: integer title: Max Sweeps Per Hour max_sweeps_per_day: type: integer title: Max Sweeps Per Day sweeps_this_hour: type: integer title: Sweeps This Hour sweeps_today: type: integer title: Sweeps Today sweeps_skipped_budget: type: integer title: Sweeps Skipped Budget type: object required: - enabled - lookback_days - include_federated - max_sweeps_per_hour - max_sweeps_per_day - sweeps_this_hour - sweeps_today - sweeps_skipped_budget title: RetroHuntSettingsOut description: What this tenant has agreed to, and what its budget has been spent on. ReviewAction: properties: action: type: string pattern: ^(approve|reject)$ title: Action notes: anyOf: - type: string - type: 'null' title: Notes type: object required: - action title: ReviewAction ReviewCommentRequest: properties: comment: type: string maxLength: 4000 minLength: 1 title: Comment type: object required: - comment title: ReviewCommentRequest ReviewEvidenceRequest: properties: decision: type: string enum: - accepted - rejected title: Decision reviewer: anyOf: - type: string - type: 'null' title: Reviewer type: object required: - decision title: ReviewEvidenceRequest RiskProfileOut: 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 external_user_ref: anyOf: - type: string - type: 'null' title: External User Ref risk_score: type: number title: Risk Score risk_tier: type: string title: Risk Tier failed_auth_24h: type: integer title: Failed Auth 24H off_hours_events_7d: type: integer title: Off Hours Events 7D data_staging_score: type: number title: Data Staging Score peer_anomaly_score: type: number title: Peer Anomaly Score privilege_delta: type: integer title: Privilege Delta is_watchlisted: type: boolean title: Is Watchlisted watchlist_reason: anyOf: - type: string - type: 'null' title: Watchlist Reason last_evaluated_at: type: string title: Last Evaluated At updated_at: type: string title: Updated At type: object required: - id - tenant_id - user_id - external_user_ref - risk_score - risk_tier - failed_auth_24h - off_hours_events_7d - data_staging_score - peer_anomaly_score - privilege_delta - is_watchlisted - watchlist_reason - last_evaluated_at - updated_at title: RiskProfileOut RoleAddIn: properties: role_name: type: string maxLength: 100 minLength: 1 title: Role Name reason: type: string maxLength: 500 minLength: 1 title: Reason type: object required: - role_name - reason title: RoleAddIn RoleIn: properties: name: type: string maxLength: 100 minLength: 2 title: Name label: anyOf: - type: string maxLength: 200 - type: 'null' title: Label description: anyOf: - type: string - type: 'null' title: Description permission_ids: items: type: string format: uuid type: array title: Permission Ids default: [] permission_names: items: type: string type: array title: Permission Names default: [] type: object required: - name title: RoleIn RoleOut: properties: id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id name: type: string title: Name label: anyOf: - type: string - type: 'null' title: Label description: anyOf: - type: string - type: 'null' title: Description is_system: type: boolean title: Is System permissions: items: $ref: '#/components/schemas/PermissionOut' type: array title: Permissions default: [] user_count: type: integer title: User Count default: 0 is_sso_default: type: boolean title: Is Sso Default default: false type: object required: - id - tenant_id - name - description - is_system title: RoleOut RoleSetIn: properties: role_names: items: type: string type: array minItems: 1 title: Role Names reason: type: string maxLength: 500 minLength: 1 title: Reason type: object required: - role_names - reason title: RoleSetIn RoleUpdate: properties: name: anyOf: - type: string maxLength: 100 - type: 'null' title: Name label: anyOf: - type: string maxLength: 200 - type: 'null' title: Label description: anyOf: - type: string - type: 'null' title: Description permission_ids: anyOf: - items: type: string format: uuid type: array - type: 'null' title: Permission Ids permission_names: anyOf: - items: type: string type: array - type: 'null' title: Permission Names type: object title: RoleUpdate description: 'The machine `name` is immutable after create: it is mirrored into users.role, resolved against the JWT static map, and written into audit resource strings; renaming a live role would strand all three. The field stays on the wire (SDK clients generated against earlier releases send it) and is validated as a no-op: omit it, or send the role''s current name — an actual rename is a 400 that says why. Edit `label` and `description` for the human-facing text.' RotateScimTokenRequest: properties: grace_hours: type: integer maximum: 168.0 minimum: 0.0 title: Grace Hours description: How long the superseded credential keeps working. Zero revokes it at once, for rotation after a disclosure. default: 24 type: object title: RotateScimTokenRequest RuleActionWire: properties: set_severity: anyOf: - type: string - type: 'null' title: Set Severity route_to: anyOf: - type: string - type: 'null' title: Route To tag: anyOf: - type: string - type: 'null' title: Tag suppress: type: boolean title: Suppress default: false type: object title: RuleActionWire RuleConditionWire: properties: field: anyOf: - type: string - type: 'null' title: Field op: anyOf: - type: string - type: 'null' title: Op value: title: Value logical: anyOf: - type: string - type: 'null' title: Logical children: items: $ref: '#/components/schemas/RuleConditionWire' type: array title: Children type: object title: RuleConditionWire description: Mirror of :class:`RuleCondition` for the wire / OpenAPI shape. RuleOverrideCreate: properties: child_tenant_id: type: string format: uuid title: Child Tenant Id rule_id: type: string format: uuid title: Rule Id action: type: string title: Action note: anyOf: - type: string - type: 'null' title: Note severity_override: anyOf: - type: string - type: 'null' title: Severity Override parameter_overrides: additionalProperties: true type: object title: Parameter Overrides default: {} type: object required: - child_tenant_id - rule_id - action title: RuleOverrideCreate RuleOverrideOut: properties: id: type: string format: uuid title: Id parent_tenant_id: type: string format: uuid title: Parent Tenant Id child_tenant_id: type: string format: uuid title: Child Tenant Id rule_id: type: string format: uuid title: Rule Id action: type: string title: Action note: anyOf: - type: string - type: 'null' title: Note severity_override: anyOf: - type: string - type: 'null' title: Severity Override parameter_overrides: additionalProperties: true type: object title: Parameter Overrides created_at: type: string title: Created At type: object required: - id - parent_tenant_id - child_tenant_id - rule_id - action - note - severity_override - parameter_overrides - created_at title: RuleOverrideOut RulePackCreate: properties: name: type: string title: Name description: anyOf: - type: string - type: 'null' title: Description category: anyOf: - type: string - type: 'null' title: Category is_default: type: boolean title: Is Default default: false type: object required: - name title: RulePackCreate RulePackOut: properties: id: type: string format: uuid title: Id name: type: string title: Name description: anyOf: - type: string - type: 'null' title: Description category: anyOf: - type: string - type: 'null' title: Category is_default: type: boolean title: Is Default created_at: type: string title: Created At updated_at: type: string title: Updated At type: object required: - id - name - description - category - is_default - created_at - updated_at title: RulePackOut RulePackRuleAdd: properties: rule_id: type: string format: uuid title: Rule Id type: object required: - rule_id title: RulePackRuleAdd RulePackUpdate: properties: name: anyOf: - type: string - type: 'null' title: Name description: anyOf: - type: string - type: 'null' title: Description category: anyOf: - type: string - type: 'null' title: Category is_default: anyOf: - type: boolean - type: 'null' title: Is Default type: object title: RulePackUpdate RuleWire: properties: id: type: string title: Id description: type: string title: Description default: '' enabled: type: boolean title: Enabled default: true priority: type: integer title: Priority default: 100 when: $ref: '#/components/schemas/RuleConditionWire' then: $ref: '#/components/schemas/RuleActionWire' type: object required: - id - when - then title: RuleWire RulesEnvelope: properties: tenant_id: type: string title: Tenant Id version: type: integer title: Version yaml: type: string title: Yaml rules: items: $ref: '#/components/schemas/RuleWire' type: array title: Rules enabled: type: boolean title: Enabled updated_at: type: string title: Updated At type: object required: - tenant_id - version - yaml - rules - enabled - updated_at title: RulesEnvelope description: 'Response payload for ``GET /rules``. The editor needs *both* the YAML source (so Monaco can show formatting + comments) and the parsed view (so the rule-builder side-panel can render structured forms). ``version`` is the engine snapshot version the parsed view came from — a monotonic integer the UI uses to detect "someone else saved while I was editing".' RunDetail: properties: id: type: string format: uuid title: Id case_id: type: string title: Case Id status: type: string title: Status model_used: anyOf: - type: string - type: 'null' title: Model Used iterations: type: integer title: Iterations total_tokens: type: integer title: Total Tokens total_cost_usd: type: number title: Total Cost Usd measured_call_count: type: integer title: Measured Call Count default: 0 estimated_cost_usd: type: number title: Estimated Cost Usd default: 0.0 estimated_call_count: type: integer title: Estimated Call Count default: 0 unpriced_call_count: type: integer title: Unpriced Call Count default: 0 started_at: type: string format: date-time title: Started At completed_at: anyOf: - type: string format: date-time - type: 'null' title: Completed At error: anyOf: - type: string - type: 'null' title: Error alert_summary: anyOf: - type: string - type: 'null' title: Alert Summary event_count: type: integer title: Event Count artifact_count: type: integer title: Artifact Count model_costs: items: $ref: '#/components/schemas/ModelCostBreakdown' type: array title: Model Costs type: object required: - id - case_id - status - model_used - iterations - total_tokens - total_cost_usd - started_at - completed_at - error - alert_summary - event_count - artifact_count - model_costs title: RunDetail description: Full run view including counts of attached children. RunEvalRequest: properties: use_active_baseline: type: boolean title: Use Active Baseline description: If true (default), the most recent active MITRE accuracy baseline is materialised to a temp file and passed to run_evals.py via --baseline so the runner can compute baseline_compare in-band. default: true max_regression_pp: type: number maximum: 50.0 minimum: 0.0 title: Max Regression Pp description: Allowed MITRE accuracy regression vs the active baseline, in percentage points. Forwarded to run_evals.py. Hard-capped at 50 to keep the regression gate meaningful. default: 1.0 timeout_seconds: type: integer maximum: 600.0 minimum: 10.0 title: Timeout Seconds description: Hard timeout for the eval subprocess. Capped at 10 minutes — the synthetic benchmark completes in under 60s on commodity hardware so anything longer is almost certainly a stuck process. default: 180 type: object title: RunEvalRequest description: 'Trigger a local run of the offline eval harness. The runner is `scripts/run_evals.py`. It is fully offline and deterministic against the 200-incident synthetic benchmark, so this endpoint can be safely called from the rule editor on every "Propose for review" action.' RunEvalResponse: properties: report: additionalProperties: true type: object title: Report exit_code: type: integer title: Exit Code duration_seconds: type: number title: Duration Seconds ran_at: type: string format: date-time title: Ran At script: type: string title: Script type: object required: - report - exit_code - duration_seconds - ran_at - script title: RunEvalResponse description: Wraps the JSON report emitted by `run_evals.py`. RunHuntRequest: properties: platform: type: string enum: - esql - spl - kql title: Platform default: esql type: object title: RunHuntRequest RunRequest: properties: payload: additionalProperties: true type: object title: Payload default: {} context: additionalProperties: true type: object title: Context default: {} type: object title: RunRequest RunResponse: properties: plugin_id: type: string title: Plugin Id result: additionalProperties: true type: object title: Result type: object required: - plugin_id - result title: RunResponse RunSavedHuntResponse: properties: id: type: string title: Id name: type: string title: Name nl_query: type: string title: Nl Query translated_query: $ref: '#/components/schemas/TranslatedQueryEnvelope' last_run_at: type: string title: Last Run At executed: type: boolean title: Executed default: false result: anyOf: - $ref: '#/components/schemas/SavedHuntRunResult' - type: 'null' execution_error: anyOf: - type: string - type: 'null' title: Execution Error unsupported_filters: items: type: string type: array title: Unsupported Filters type: object required: - id - name - nl_query - translated_query - last_run_at title: RunSavedHuntResponse description: 'Synchronous run result returned by ``POST /saved-hunts/{id}/run``. This endpoint used to re-translate the question, stamp ``last_run_at`` and return — executing nothing. The UI then displayed "last run just now" for a hunt that had queried no data at all, which is indistinguishable from a hunt that ran and found nothing. It now runs the compiled query against AiSOC''s own event lake, which is where every connector''s events are archived. ``executed`` says plainly whether rows were fetched, so a lake that is unreachable or unconfigured reports that instead of looking like a clean run with zero hits.' RunSummary: properties: id: type: string format: uuid title: Id case_id: type: string title: Case Id status: type: string title: Status model_used: anyOf: - type: string - type: 'null' title: Model Used iterations: type: integer title: Iterations total_tokens: type: integer title: Total Tokens total_cost_usd: type: number title: Total Cost Usd measured_call_count: type: integer title: Measured Call Count default: 0 estimated_cost_usd: type: number title: Estimated Cost Usd default: 0.0 estimated_call_count: type: integer title: Estimated Call Count default: 0 unpriced_call_count: type: integer title: Unpriced Call Count default: 0 started_at: type: string format: date-time title: Started At completed_at: anyOf: - type: string format: date-time - type: 'null' title: Completed At error: anyOf: - type: string - type: 'null' title: Error type: object required: - id - case_id - status - model_used - iterations - total_tokens - total_cost_usd - started_at - completed_at - error title: RunSummary description: Compact view of a run for list endpoints. SLAConfigOut: properties: id: type: string format: uuid title: Id severity: type: string title: Severity mttd_target: type: integer title: Mttd Target mttr_target: type: integer title: Mttr Target mttc_target: type: integer title: Mttc Target updated_at: type: string format: date-time title: Updated At type: object required: - id - severity - mttd_target - mttr_target - mttc_target - updated_at title: SLAConfigOut SLAConfigUpdate: properties: mttd_target: type: integer maximum: 10080.0 minimum: 1.0 title: Mttd Target description: Minutes mttr_target: type: integer maximum: 10080.0 minimum: 1.0 title: Mttr Target description: Minutes mttc_target: type: integer maximum: 10080.0 minimum: 1.0 title: Mttc Target description: Minutes type: object required: - mttd_target - mttr_target - mttc_target title: SLAConfigUpdate SLAEventCreate: properties: alert_id: type: string format: uuid title: Alert Id severity: type: string title: Severity event_type: type: string title: Event Type occurred_at: anyOf: - type: string format: date-time - type: 'null' title: Occurred At metadata: additionalProperties: true type: object title: Metadata type: object required: - alert_id - severity - event_type title: SLAEventCreate SLAEventOut: properties: id: type: string format: uuid title: Id alert_id: type: string format: uuid title: Alert Id severity: type: string title: Severity event_type: type: string title: Event Type occurred_at: type: string format: date-time title: Occurred At metadata: additionalProperties: true type: object title: Metadata type: object required: - id - alert_id - severity - event_type - occurred_at - metadata title: SLAEventOut SOCInsightsResponse: properties: window: type: string enum: - 24h - 7d - 30d title: Window generated_at: type: string format: date-time title: Generated At tenant_id: type: string format: uuid title: Tenant Id tiles: items: $ref: '#/components/schemas/InsightTile' type: array title: Tiles manual_investigation_minutes: type: integer title: Manual Investigation Minutes default: 45 type: object required: - window - generated_at - tenant_id - tiles title: SOCInsightsResponse description: Aggregated SOC insights payload. SOCKpis: properties: mttd_hours: type: number title: Mttd Hours mttr_hours: type: number title: Mttr Hours mttc_hours: type: number title: Mttc Hours false_positive_rate: type: number title: False Positive Rate escalation_rate: type: number title: Escalation Rate alert_volume_7d: type: integer title: Alert Volume 7D cases_opened_7d: type: integer title: Cases Opened 7D cases_closed_7d: type: integer title: Cases Closed 7D analyst_overrides_7d: type: integer title: Analyst Overrides 7D mttd_sample_count: type: integer title: Mttd Sample Count default: 0 mttr_sample_count: type: integer title: Mttr Sample Count default: 0 mttc_sample_count: type: integer title: Mttc Sample Count default: 0 false_positive_rate_sample_count: type: integer title: False Positive Rate Sample Count default: 0 escalation_rate_sample_count: type: integer title: Escalation Rate Sample Count default: 0 type: object required: - mttd_hours - mttr_hours - mttc_hours - false_positive_rate - escalation_rate - alert_volume_7d - cases_opened_7d - cases_closed_7d - analyst_overrides_7d title: SOCKpis SOCMetrics: properties: kpis: $ref: '#/components/schemas/SOCKpis' attack_heatmap: items: $ref: '#/components/schemas/AttackHeatmapCell' type: array title: Attack Heatmap calibration_curve: items: $ref: '#/components/schemas/CalibrationBucket' type: array title: Calibration Curve type: object required: - kpis - attack_heatmap - calibration_curve title: SOCMetrics STIXBundle: properties: type: type: string title: Type default: bundle id: type: string title: Id spec_version: type: string title: Spec Version default: '2.1' created: type: string title: Created objects: items: additionalProperties: true type: object type: array title: Objects type: object required: - id - created - objects title: STIXBundle STIXBundleCreate: properties: objects: items: additionalProperties: true type: object type: array title: Objects type: object required: - objects title: STIXBundleCreate STIXBundleWithPush: properties: type: type: string title: Type default: bundle id: type: string title: Id spec_version: type: string title: Spec Version default: '2.1' created: type: string title: Created objects: items: additionalProperties: true type: object type: array title: Objects misp: anyOf: - $ref: '#/components/schemas/MispPushResult' - type: 'null' type: object required: - id - created - objects title: STIXBundleWithPush STIXIndicator: properties: type: type: string title: Type default: indicator spec_version: type: string title: Spec Version default: '2.1' id: type: string title: Id created: type: string title: Created modified: type: string title: Modified name: type: string title: Name description: anyOf: - type: string - type: 'null' title: Description indicator_types: items: type: string type: array title: Indicator Types default: [] pattern: type: string title: Pattern pattern_type: type: string title: Pattern Type default: stix valid_from: type: string title: Valid From valid_until: anyOf: - type: string - type: 'null' title: Valid Until confidence: anyOf: - type: integer maximum: 100.0 minimum: 0.0 - type: 'null' title: Confidence labels: items: type: string type: array title: Labels default: [] type: object required: - id - created - modified - name - pattern - valid_from title: STIXIndicator STIXIndicatorCreate: properties: name: type: string title: Name description: anyOf: - type: string - type: 'null' title: Description indicator_types: items: type: string type: array title: Indicator Types default: [] pattern: type: string title: Pattern pattern_type: type: string title: Pattern Type default: stix valid_from: anyOf: - type: string - type: 'null' title: Valid From valid_until: anyOf: - type: string - type: 'null' title: Valid Until confidence: anyOf: - type: integer maximum: 100.0 minimum: 0.0 - type: 'null' title: Confidence labels: items: type: string type: array title: Labels default: [] type: object required: - name - pattern title: STIXIndicatorCreate STIXIndicatorWithPush: properties: type: type: string title: Type default: indicator spec_version: type: string title: Spec Version default: '2.1' id: type: string title: Id created: type: string title: Created modified: type: string title: Modified name: type: string title: Name description: anyOf: - type: string - type: 'null' title: Description indicator_types: items: type: string type: array title: Indicator Types default: [] pattern: type: string title: Pattern pattern_type: type: string title: Pattern Type default: stix valid_from: type: string title: Valid From valid_until: anyOf: - type: string - type: 'null' title: Valid Until confidence: anyOf: - type: integer maximum: 100.0 minimum: 0.0 - type: 'null' title: Confidence labels: items: type: string type: array title: Labels default: [] misp: anyOf: - $ref: '#/components/schemas/MispPushResult' - type: 'null' type: object required: - id - created - modified - name - pattern - valid_from title: STIXIndicatorWithPush SampleDataResponse: properties: accepted: type: integer title: Accepted rejected: type: integer title: Rejected scenarios: items: additionalProperties: true type: object type: array title: Scenarios note: type: string title: Note type: object required: - accepted - rejected - scenarios - note title: SampleDataResponse SanctionRequest: properties: sanctioned: anyOf: - type: boolean - type: 'null' title: Sanctioned description: true = approved, false = reviewed and refused, null = back to undecided. The three are distinct. note: anyOf: - type: string maxLength: 500 - type: 'null' title: Note type: object required: - sanctioned title: SanctionRequest SandboxResultResponse: properties: outcome: type: string enum: - known - pending - not_seen - upload_refused - could_not_check title: Outcome provider: type: string title: Provider detail: type: string title: Detail sha256: anyOf: - type: string - type: 'null' title: Sha256 uploaded: type: boolean title: Uploaded default: false report: anyOf: - additionalProperties: true type: object - type: 'null' title: Report handle: anyOf: - type: string - type: 'null' title: Handle poll_after_seconds: anyOf: - type: integer - type: 'null' title: Poll After Seconds decision: anyOf: - additionalProperties: true type: object - type: 'null' title: Decision warnings: items: type: string type: array title: Warnings type: object required: - outcome - provider - detail title: SandboxResultResponse SaveSkillRequest: properties: source_yaml: type: string maxLength: 32768 minLength: 1 title: Source Yaml type: object required: - source_yaml title: SaveSkillRequest SavedHuntModel: properties: id: type: string title: Id name: type: string title: Name nl_query: type: string title: Nl Query translated_query: $ref: '#/components/schemas/TranslatedQueryEnvelope' language: type: string enum: - esql - kql - spl title: Language schedule: anyOf: - type: string - type: 'null' title: Schedule last_run_at: anyOf: - type: string - type: 'null' title: Last Run At created_at: type: string title: Created At updated_at: type: string title: Updated At created_by: anyOf: - type: string - type: 'null' title: Created By type: object required: - id - name - nl_query - translated_query - language - created_at - updated_at title: SavedHuntModel description: Wire format for a saved hunt row. SavedHuntRunResult: properties: columns: items: type: string type: array title: Columns rows: items: items: {} type: array type: array title: Rows total_rows: type: integer title: Total Rows default: 0 took_ms: anyOf: - type: integer - type: 'null' title: Took Ms type: object title: SavedHuntRunResult description: Rows the hunt actually returned. SavedViewModel: properties: id: type: string title: Id view_type: type: string title: View Type name: type: string title: Name filters: additionalProperties: true type: object title: Filters columns: anyOf: - items: {} type: array - additionalProperties: true type: object - type: 'null' title: Columns is_default: type: boolean title: Is Default default: false created_at: type: string title: Created At updated_at: type: string title: Updated At type: object required: - id - view_type - name - created_at - updated_at title: SavedViewModel description: Wire format for a saved view row. ScanRequest: properties: tenant_id: type: string format: uuid title: Tenant Id org_query: anyOf: - type: string - type: 'null' title: Org Query ip_targets: items: type: string type: array title: Ip Targets default: [] asset_type: anyOf: - $ref: '#/components/schemas/ExternalAssetType' - type: 'null' type: object required: - tenant_id title: ScanRequest ScanResponse: properties: assets: type: integer title: Assets errors: items: type: string type: array title: Errors skipped: additionalProperties: type: string type: object title: Skipped type: object required: - assets title: ScanResponse ScanRunOut: properties: id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id cloud_provider: type: string title: Cloud Provider cloud_account: anyOf: - type: string - type: 'null' title: Cloud Account started_at: type: string title: Started At completed_at: anyOf: - type: string - type: 'null' title: Completed At status: type: string title: Status findings_total: type: integer title: Findings Total findings_new: type: integer title: Findings New findings_closed: type: integer title: Findings Closed error_message: anyOf: - type: string - type: 'null' title: Error Message type: object required: - id - tenant_id - cloud_provider - cloud_account - started_at - completed_at - status - findings_total - findings_new - findings_closed - error_message title: ScanRunOut ScimTokenOut: properties: id: type: string format: uuid title: Id name: type: string title: Name prefix: type: string title: Prefix org_id: anyOf: - type: string format: uuid - type: 'null' title: Org Id 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 expires_at: anyOf: - type: string format: date-time - type: 'null' title: Expires At revoked_at: anyOf: - type: string format: date-time - type: 'null' title: Revoked At rotated_from_id: anyOf: - type: string format: uuid - type: 'null' title: Rotated From Id active: type: boolean title: Active type: object required: - id - name - prefix - org_id - created_at - last_used_at - expires_at - revoked_at - rotated_from_id - active title: ScimTokenOut SessionTokens: 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 default: 1800 type: object required: - access_token - refresh_token title: SessionTokens SetupStep: properties: key: type: string title: Key label: type: string title: Label done: type: boolean title: Done why: type: string title: Why href: anyOf: - type: string - type: 'null' title: Href detail: anyOf: - type: string - type: 'null' title: Detail type: object required: - key - label - done - why title: SetupStep description: One thing a tenant has or has not done. ShadowModeEntry: properties: alert_class: type: string title: Alert Class enabled: type: boolean title: Enabled enabled_at: anyOf: - type: string - type: 'null' title: Enabled At updated_at: anyOf: - type: string - type: 'null' title: Updated At updated_by: anyOf: - type: string - type: 'null' title: Updated By type: object required: - alert_class - enabled title: ShadowModeEntry ShadowModeResponse: properties: tenant_id: type: string title: Tenant Id entries: items: $ref: '#/components/schemas/ShadowModeEntry' type: array title: Entries type: object required: - tenant_id - entries title: ShadowModeResponse ShadowModeUpdateRequest: properties: enabled: type: boolean title: Enabled type: object required: - enabled title: ShadowModeUpdateRequest SiemSearchRequest: properties: indicator_type: type: string title: Indicator Type description: One of the types GET /agent-tools/backends publishes. value: type: string maxLength: 2048 minLength: 1 title: Value since_hours: type: integer maximum: 168.0 minimum: 1.0 title: Since Hours default: 24 limit: type: integer maximum: 40.0 minimum: 1.0 title: Limit default: 40 type: object required: - indicator_type - value title: SiemSearchRequest description: 'A structured query. Deliberately not a query. There is no ``query`` field, no ``free_text`` field and no ``field`` field, and their absence is the contract rather than an omission. ``indicator_type`` is validated against a closed set and ``value`` against the shape that type requires, so a model cannot pass query text by claiming it is an indicator.' SkillBacktestRequest: properties: connector_id: type: string format: uuid title: Connector Id since: anyOf: - type: string format: date-time - type: 'null' title: Since until: anyOf: - type: string format: date-time - type: 'null' title: Until train_fraction: type: number exclusiveMaximum: 1.0 exclusiveMinimum: 0.0 title: Train Fraction default: 0.7 limit: type: integer maximum: 2000.0 minimum: 1.0 title: Limit default: 1000 bootstrap_seed: type: integer title: Bootstrap Seed default: 20260926 bootstrap_resamples: type: integer title: Bootstrap Resamples default: 1000 type: object required: - connector_id title: SkillBacktestRequest description: Which connector and window to grade the skill over. SkillBacktestResponse: properties: skill_id: type: string title: Skill Id version: type: integer title: Version baseline_evaluation_id: type: string title: Baseline Evaluation Id candidate_evaluation_id: type: string title: Candidate Evaluation Id note: type: string title: Note type: object required: - skill_id - version - baseline_evaluation_id - candidate_evaluation_id - note title: SkillBacktestResponse description: The two runs the console polls, and how to read them together. SkillListResponse: properties: skills: items: $ref: '#/components/schemas/SkillModel' type: array title: Skills available_tools: $ref: '#/components/schemas/AvailableTools' type: object required: - skills - available_tools title: SkillListResponse SkillModel: properties: skill_id: type: string title: Skill Id version: type: integer title: Version status: type: string title: Status name: type: string title: Name owner: type: string title: Owner expires_at: type: string format: date-time title: Expires At expired: type: boolean title: Expired body: additionalProperties: true type: object title: Body source_yaml: type: string title: Source Yaml backtest_evaluation_id: anyOf: - type: string - type: 'null' title: Backtest Evaluation Id backtest_baseline_id: anyOf: - type: string - type: 'null' title: Backtest Baseline Id backtest_version: anyOf: - type: integer - type: 'null' title: Backtest Version activated_at: anyOf: - type: string format: date-time - type: 'null' title: Activated At 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: - skill_id - version - status - name - owner - expires_at - expired - body - source_yaml title: SkillModel description: One skill as the console sees it. SkillVersionModel: properties: skill_id: type: string title: Skill Id version: type: integer title: Version name: type: string title: Name owner: type: string title: Owner expires_at: type: string format: date-time title: Expires At body: additionalProperties: true type: object title: Body source_yaml: type: string title: Source Yaml backtest_evaluation_id: anyOf: - type: string - type: 'null' title: Backtest Evaluation Id backtest_baseline_id: anyOf: - type: string - type: 'null' title: Backtest Baseline Id authored_at: anyOf: - type: string format: date-time - type: 'null' title: Authored At activated_at: anyOf: - type: string format: date-time - type: 'null' title: Activated At retired_at: anyOf: - type: string format: date-time - type: 'null' title: Retired At type: object required: - skill_id - version - name - owner - expires_at - body - source_yaml title: SkillVersionModel description: One immutable version, which is what a recorded ``skill@vN`` resolves to. SourceLinkResponse: properties: vendor: type: string title: Vendor external_id: type: string title: External Id external_url: anyOf: - type: string - type: 'null' title: External Url last_disposition: anyOf: - type: string - type: 'null' title: Last Disposition last_writeback_action: anyOf: - type: string - type: 'null' title: Last Writeback Action last_writeback_status: anyOf: - type: string - type: 'null' title: Last Writeback Status executed: type: boolean title: Executed default: false type: object required: - vendor - external_id title: SourceLinkResponse SourceStat: properties: name: type: string title: Name count: type: integer title: Count status: type: string title: Status type: object required: - name - count - status title: SourceStat SourceThreat: properties: source: type: string title: Source count: type: integer title: Count type: object required: - source - count title: SourceThreat SourceVerdict: properties: connector_id: type: string format: uuid title: Connector Id connector_name: type: string title: Connector Name connector_type: type: string title: Connector Type status: type: string title: Status row_count: type: integer title: Row Count default: 0 duration_ms: type: integer title: Duration Ms default: 0 error: anyOf: - type: string - type: 'null' title: Error type: object required: - connector_id - connector_name - connector_type - status title: SourceVerdict description: "Per-backend outcome for a federated call.\n\n``status`` is one\ \ of:\n * ``ok`` — backend returned rows (possibly zero).\n * ``error``\ \ — backend raised something we caught; ``error`` field\n carries the human\ \ message. Other backends may still have\n succeeded.\n * ``unsupported``\ \ — connector type doesn't speak federated search\n (kept for forward-compat;\ \ today we pre-filter so this is rare)." SourceWritebackRequest: properties: disposition: type: string title: Disposition description: Canonical AiSOC disposition. Anything unrecognised is refused, not guessed. confidence: anyOf: - type: number maximum: 1.0 minimum: 0.0 - type: 'null' title: Confidence rationale: type: string maxLength: 4000 title: Rationale default: '' tenant_id: anyOf: - type: string format: uuid - type: 'null' title: Tenant Id type: object required: - disposition title: SourceWritebackRequest description: What the caller believes about the alert. SsoConnectionIn: properties: provider: type: string title: Provider issuer: type: string maxLength: 512 minLength: 1 title: Issuer display_name: anyOf: - type: string maxLength: 200 - type: 'null' title: Display Name enabled: type: boolean title: Enabled default: false group_role_mapping: additionalProperties: type: string type: object title: Group Role Mapping default_role: type: string title: Default Role default: viewer metadata_url: anyOf: - type: string maxLength: 1024 - type: 'null' title: Metadata Url metadata_xml: anyOf: - type: string - type: 'null' title: Metadata Xml allowed_email_domains: type: string maxLength: 2000 title: Allowed Email Domains default: '' jit_provisioning: type: boolean title: Jit Provisioning default: true group_role_mode: type: string title: Group Role Mode default: first_login_only login_label: type: string maxLength: 80 title: Login Label default: '' type: object required: - provider - issuer title: SsoConnectionIn SsoConnectionOut: properties: id: type: string title: Id tenant_id: type: string title: Tenant Id provider: type: string title: Provider issuer: type: string title: Issuer display_name: anyOf: - type: string - type: 'null' title: Display Name enabled: type: boolean title: Enabled group_role_mapping: additionalProperties: type: string type: object title: Group Role Mapping default_role: type: string title: Default Role allowed_email_domains: type: string title: Allowed Email Domains jit_provisioning: type: boolean title: Jit Provisioning group_role_mode: type: string title: Group Role Mode login_label: type: string title: Login Label metadata_url: anyOf: - type: string - type: 'null' title: Metadata Url has_metadata_xml: type: boolean title: Has Metadata Xml created_at: anyOf: - type: string - type: 'null' title: Created At updated_at: anyOf: - type: string - type: 'null' title: Updated At type: object required: - id - tenant_id - provider - issuer - display_name - enabled - group_role_mapping - default_role - allowed_email_domains - jit_provisioning - group_role_mode - login_label - metadata_url - has_metadata_xml - created_at - updated_at title: SsoConnectionOut StartReplayRequest: properties: connector_id: type: string format: uuid title: Connector Id since: anyOf: - type: string format: date-time - type: 'null' title: Since until: anyOf: - type: string format: date-time - type: 'null' title: Until train_fraction: type: number exclusiveMaximum: 1.0 exclusiveMinimum: 0.0 title: Train Fraction default: 0.7 limit: type: integer maximum: 2000.0 minimum: 1.0 title: Limit default: 1000 bootstrap_seed: type: integer title: Bootstrap Seed default: 20260926 bootstrap_resamples: type: integer maximum: 20000.0 minimum: 0.0 title: Bootstrap Resamples default: 1000 type: object required: - connector_id title: StartReplayRequest description: Which connector, which window, and how the report should be computed. StepDispatchRequest: properties: capability: type: string title: Capability description: Response verb, e.g. 'isolate_host'. Must be a registered capability. target: type: string maxLength: 512 title: Target description: Host, user, address or id the verb acts on. default: '' params: additionalProperties: true type: object title: Params vendor_id: type: string maxLength: 64 title: Vendor Id description: Pin a vendor. Honoured only if the tenant has it configured. default: '' confidence: anyOf: - type: number maximum: 1.0 minimum: 0.0 - type: 'null' title: Confidence dry_run: type: boolean title: Dry Run default: true playbook_run_id: type: string maxLength: 64 title: Playbook Run Id default: '' playbook_step_id: type: string maxLength: 64 title: Playbook Step Id default: '' tenant_id: anyOf: - type: string format: uuid - type: 'null' title: Tenant Id type: object required: - capability title: StepDispatchRequest description: One playbook step, expressed as the verb it names. StorageCostProjection: properties: measured: type: boolean title: Measured unmeasured_reason: anyOf: - type: string - type: 'null' title: Unmeasured Reason raw_bytes_measured: anyOf: - type: integer - type: 'null' title: Raw Bytes Measured events_measured: anyOf: - type: integer - type: 'null' title: Events Measured raw_tb_per_day: anyOf: - type: number - type: 'null' title: Raw Tb Per Day monthly_usd: anyOf: - type: number - type: 'null' title: Monthly Usd usd_per_raw_tb_ingested: anyOf: - type: number - type: 'null' title: Usd Per Raw Tb Ingested tiers: items: $ref: '#/components/schemas/StorageTierProjection' type: array title: Tiers compression_ratio: type: number title: Compression Ratio default: 8.0 disclaimer: type: string title: Disclaimer default: Projected from your measured ingest volume using the committed storage cost model (docs/decisions/storage-cost-model.json) at reference list prices. This is a model, not your provider's bill — verify against your own region and negotiated rates. type: object required: - measured title: StorageCostProjection description: 'Projected storage cost for a tenant, or an honest statement that there is none. ``measured`` is the field that matters. When it is false every money field is ``None`` — not ``0.0`` — and ``unmeasured_reason`` says why.' StorageTierProjection: properties: tier: type: string title: Tier retention_days: type: integer title: Retention Days resident_gb: type: number title: Resident Gb rate_usd_per_gb_month: type: number title: Rate Usd Per Gb Month monthly_usd: type: number title: Monthly Usd type: object required: - tier - retention_days - resident_gb - rate_usd_per_gb_month - monthly_usd title: StorageTierProjection description: One retention tier's share of the projected monthly storage cost. SubmissionResponse: properties: id: type: string format: uuid title: Id artifact_kind: type: string title: Artifact Kind sender: anyOf: - type: string - type: 'null' title: Sender subject: anyOf: - type: string - type: 'null' title: Subject urls: items: type: string type: array title: Urls verdict: type: string title: Verdict confidence: anyOf: - type: number - type: 'null' title: Confidence indicators: items: additionalProperties: true type: object type: array title: Indicators mitre_technique: anyOf: - type: string - type: 'null' title: Mitre Technique case_id: anyOf: - type: string format: uuid - type: 'null' title: Case Id submitted_at: type: string format: date-time title: Submitted At triaged_at: anyOf: - type: string format: date-time - type: 'null' title: Triaged At type: object required: - id - artifact_kind - sender - subject - urls - verdict - confidence - indicators - mitre_technique - case_id - submitted_at - triaged_at title: SubmissionResponse SubmitFileRequest: properties: file_base64: type: string title: File Base64 description: The file contents, base64-encoded. file_name: type: string maxLength: 512 title: File Name provider: anyOf: - type: string - type: 'null' title: Provider confirm_upload: type: boolean title: Confirm Upload default: false type: object required: - file_base64 - file_name title: SubmitFileRequest SubmitRequest: properties: artifact_kind: type: string enum: - email - url - attachment - domain title: Artifact Kind default: email raw_content: anyOf: - type: string - type: 'null' title: Raw Content description: Raw email source or URL to analyse. sender: anyOf: - type: string - type: 'null' title: Sender subject: anyOf: - type: string - type: 'null' title: Subject urls: items: type: string type: array title: Urls attachment_hashes: items: type: string type: array title: Attachment Hashes description: 'SHA-256 digests of the message''s attachments. Digests only: the attachment itself is never accepted here, because this route runs unattended on submitted mail and an unattended path that can upload is one misconfiguration away from disclosing every attachment a tenant receives. To have a file analysed, POST it to /sandbox/files, which enforces the tenant''s upload policy.' type: object title: SubmitRequest SubmitUrlRequest: properties: url: type: string maxLength: 2048 title: Url provider: anyOf: - type: string - type: 'null' title: Provider type: object required: - url title: SubmitUrlRequest SuppressRequest: properties: reason: type: string title: Reason type: object required: - reason title: SuppressRequest SyncResult: properties: posted: type: integer title: Posted error: anyOf: - type: string - type: 'null' title: Error type: object required: - posted - error title: SyncResult TAXIICollection: properties: id: type: string title: Id title: type: string title: Title description: type: string title: Description can_read: type: boolean title: Can Read default: true can_write: type: boolean title: Can Write default: false media_types: items: type: string type: array title: Media Types default: - application/stix+json;version=2.1 type: object required: - id - title - description title: TAXIICollection TAXIICollectionListResponse: properties: items: items: $ref: '#/components/schemas/TAXIICollection' type: array title: Items total: type: integer title: Total type: object required: - items - total title: TAXIICollectionListResponse TacticConfidence: properties: tactic: type: string title: Tactic rules: type: integer title: Rules activeRules: type: integer title: Activerules avgConfidence: type: number title: Avgconfidence avgConfidenceActive: type: number title: Avgconfidenceactive type: object required: - tactic - rules - activeRules - avgConfidence - avgConfidenceActive title: TacticConfidence description: 'Average rule confidence within one MITRE tactic. Useful for the "where is my coverage weakest?" question — analysts plot tactics as bars and immediately see that, e.g. *exfiltration* averages 38/100 while *execution* averages 78/100.' TaskResponse: properties: id: type: string title: Id title: type: string title: Title status: type: string enum: - todo - in_progress - done title: Status assignee: anyOf: - type: string - type: 'null' title: Assignee dueAt: anyOf: - type: string format: date-time - type: 'null' title: Dueat createdAt: type: string format: date-time title: Createdat type: object required: - id - title - status - createdAt title: TaskResponse TemplateCreate: properties: name: type: string title: Name report_type: type: string title: Report Type cron_schedule: anyOf: - type: string - type: 'null' title: Cron Schedule timezone: type: string title: Timezone default: UTC sections: items: additionalProperties: true type: object type: array title: Sections recipients: anyOf: - items: type: string type: array - type: 'null' title: Recipients output_format: type: string title: Output Format default: pdf type: object required: - name - report_type title: TemplateCreate TemplateOut: properties: name: type: string title: Name report_type: type: string title: Report Type cron_schedule: anyOf: - type: string - type: 'null' title: Cron Schedule timezone: type: string title: Timezone default: UTC sections: items: additionalProperties: true type: object type: array title: Sections recipients: anyOf: - items: type: string type: array - type: 'null' title: Recipients output_format: type: string title: Output Format default: pdf id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id is_enabled: type: boolean title: Is Enabled last_run_at: anyOf: - type: string - type: 'null' title: Last Run At created_at: type: string title: Created At updated_at: type: string title: Updated At type: object required: - name - report_type - id - tenant_id - is_enabled - created_at - updated_at title: TemplateOut TenantDeletionRequest: properties: dry_run: type: boolean title: Dry Run default: true confirm_tenant_id: anyOf: - type: string format: uuid - type: 'null' title: Confirm Tenant Id type: object title: TenantDeletionRequest description: 'Offboarding request. ``confirm_tenant_id`` must equal the tenant being erased. Typing the id is the only thing standing between "preview the erase" and "erase", and this endpoint has no undo.' TenantGrant: properties: tenant_ids: items: type: string format: uuid type: array title: Tenant Ids type: object required: - tenant_ids title: TenantGrant TenantHeaderResponse: properties: id: type: string format: uuid title: Id name: type: string title: Name mssp_role: anyOf: - type: string - type: 'null' title: Mssp Role parent_tenant_id: anyOf: - type: string format: uuid - type: 'null' title: Parent Tenant Id type: object required: - id - name - mssp_role - parent_tenant_id title: TenantHeaderResponse description: 'Minimal tenant identity payload — safe for *any* authenticated user. Used by the SOC console TopBar to render the tenant switcher and role badge (Workstream 5). Intentionally excludes `plan`, `settings`, and `limits` so it does not leak privileged config to viewer/analyst roles.' TenantListEntry: properties: id: type: string title: Id name: type: string title: Name slug: type: string title: Slug plan: type: string title: Plan is_active: type: boolean title: Is Active is_managed: type: boolean title: Is Managed provisioned_from_waitlist_id: anyOf: - type: string - type: 'null' title: Provisioned From Waitlist Id provisioned_at: anyOf: - type: string - type: 'null' title: Provisioned At created_at: type: string title: Created At type: object required: - id - name - slug - plan - is_active - is_managed - provisioned_from_waitlist_id - provisioned_at - created_at title: TenantListEntry TenantListResponse: properties: tenants: items: $ref: '#/components/schemas/TenantListEntry' type: array title: Tenants total: type: integer title: Total type: object required: - tenants - total title: TenantListResponse TenantNoteCreate: properties: child_id: type: string format: uuid title: Child Id body: type: string title: Body type: object required: - child_id - body title: TenantNoteCreate TenantNoteOut: properties: child_id: type: string format: uuid title: Child Id body: type: string title: Body id: type: string format: uuid title: Id parent_id: type: string format: uuid title: Parent Id author_id: anyOf: - type: string format: uuid - type: 'null' title: Author Id created_at: type: string title: Created At type: object required: - child_id - body - id - parent_id - author_id - created_at title: TenantNoteOut TenantProvisionRequest: properties: waitlist_entry_id: type: string format: uuid title: Waitlist Entry Id seed_demo: type: boolean title: Seed Demo default: true invite_base_url: anyOf: - type: string maxLength: 512 - type: 'null' title: Invite Base Url type: object required: - waitlist_entry_id title: TenantProvisionRequest description: 'Body of the provision POST. ``waitlist_entry_id`` is the only required field. The optional ``seed_demo`` flag lets the operator turn off the starter dataset for tenants where the customer wants a clean slate.' TenantProvisionResponse: properties: tenant_id: type: string title: Tenant Id tenant_slug: type: string title: Tenant Slug tenant_name: type: string title: Tenant Name waitlist_entry_id: type: string title: Waitlist Entry Id admin_user: $ref: '#/components/schemas/InitialAdminUserWire' admin_invite: $ref: '#/components/schemas/AdminInviteWire' demo_seeded: type: boolean title: Demo Seeded aisoc_credential_key_fingerprint: type: string title: Aisoc Credential Key Fingerprint type: object required: - tenant_id - tenant_slug - tenant_name - waitlist_entry_id - admin_user - admin_invite - demo_seeded - aisoc_credential_key_fingerprint title: TenantProvisionResponse description: 'JSON projection of :class:`ProvisionResult`. ``aisoc_credential_key_fingerprint`` is surfaced so the support UI can confirm at a glance which key the tenant is on. The *plaintext* key is never returned; it''s only readable from the process environment where it was minted.' TenantResponse: properties: id: type: string format: uuid title: Id name: type: string title: Name slug: type: string title: Slug plan: type: string title: Plan is_active: type: boolean title: Is Active settings: additionalProperties: true type: object title: Settings limits: additionalProperties: true type: object title: Limits mssp_role: anyOf: - type: string - type: 'null' title: Mssp Role parent_tenant_id: anyOf: - type: string format: uuid - type: 'null' title: Parent Tenant Id created_at: type: string format: date-time title: Created At type: object required: - id - name - slug - plan - is_active - settings - limits - created_at title: TenantResponse TestBody: properties: language: type: string title: Language body: type: string title: Body sample: anyOf: - type: string - type: 'null' title: Sample type: object required: - language - body title: TestBody TestConnectionRequest: properties: connector_type: type: string maxLength: 100 minLength: 1 title: Connector Type auth_config: additionalProperties: true type: object title: Auth Config connector_config: additionalProperties: true type: object title: Connector Config type: object required: - connector_type title: TestConnectionRequest description: 'Inline credential-test payload used by the wizard *before* saving. The wizard collects credentials in-browser, calls this endpoint, and only persists the connector if the test succeeds. We intentionally do *not* let the wizard test against an existing instance — that''s a separate path (:func:`test_existing_connector`) that decrypts the stored creds first.' TestResponse: properties: matches: type: integer title: Matches preview: items: $ref: '#/components/schemas/HuntPreviewItem' type: array title: Preview type: object required: - matches - preview title: TestResponse ThreatActorCreate: properties: name: type: string title: Name aliases: anyOf: - items: type: string type: array - type: 'null' title: Aliases motivation: anyOf: - type: string - type: 'null' title: Motivation sophistication: anyOf: - type: string - type: 'null' title: Sophistication country_of_origin: anyOf: - type: string - type: 'null' title: Country Of Origin target_sectors: anyOf: - items: type: string type: array - type: 'null' title: Target Sectors ttps: anyOf: - items: type: string type: array - type: 'null' title: Ttps description: anyOf: - type: string - type: 'null' title: Description first_observed: anyOf: - type: string format: date-time - type: 'null' title: First Observed last_activity: anyOf: - type: string format: date-time - type: 'null' title: Last Activity context: additionalProperties: true type: object title: Context type: object required: - name title: ThreatActorCreate ThreatActorOut: properties: name: type: string title: Name aliases: anyOf: - items: type: string type: array - type: 'null' title: Aliases motivation: anyOf: - type: string - type: 'null' title: Motivation sophistication: anyOf: - type: string - type: 'null' title: Sophistication country_of_origin: anyOf: - type: string - type: 'null' title: Country Of Origin target_sectors: anyOf: - items: type: string type: array - type: 'null' title: Target Sectors ttps: anyOf: - items: type: string type: array - type: 'null' title: Ttps description: anyOf: - type: string - type: 'null' title: Description first_observed: anyOf: - type: string format: date-time - type: 'null' title: First Observed last_activity: anyOf: - type: string format: date-time - type: 'null' title: Last Activity context: additionalProperties: true type: object title: Context id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id is_active: type: boolean title: Is Active created_at: type: string title: Created At type: object required: - name - id - tenant_id - is_active - created_at title: ThreatActorOut ThresholdTriple: properties: auto: type: number maximum: 1.0 minimum: 0.0 title: Auto review: type: number maximum: 1.0 minimum: 0.0 title: Review escalation: type: number maximum: 1.0 minimum: 0.0 title: Escalation type: object required: - auto - review - escalation title: ThresholdTriple description: 'Three confidence cutoffs for one action. Invariant: ``escalation <= review <= auto`` and all are in ``[0.0, 1.0]``.' ThresholdUpdateRequest: properties: auto: type: number maximum: 1.0 minimum: 0.0 title: Auto review: type: number maximum: 1.0 minimum: 0.0 title: Review escalation: type: number maximum: 1.0 minimum: 0.0 title: Escalation reason: anyOf: - type: string maxLength: 500 - type: 'null' title: Reason description: Free-text justification — surfaced in the audit log. type: object required: - auto - review - escalation title: ThresholdUpdateRequest ThresholdUpdateResponse: properties: action: type: string title: Action thresholds: $ref: '#/components/schemas/ThresholdTriple' updated_at: type: string title: Updated At updated_by: type: string title: Updated By type: object required: - action - thresholds - updated_at - updated_by title: ThresholdUpdateResponse TimelineDecision: properties: reason: anyOf: - type: string - type: 'null' title: Reason confidence: anyOf: - type: number - type: 'null' title: Confidence next_phase: anyOf: - type: string - type: 'null' title: Next Phase tool_name: anyOf: - type: string - type: 'null' title: Tool Name tool_args: anyOf: - additionalProperties: true type: object - type: 'null' title: Tool Args tool_result_summary: anyOf: - type: string - type: 'null' title: Tool Result Summary type: object required: - reason - confidence - next_phase - tool_name - tool_args - tool_result_summary title: TimelineDecision description: Agent decision provenance annotation for a single timeline node. TimelineNode: properties: seq: type: integer title: Seq ts: type: string format: date-time title: Ts kind: type: string title: Kind agent: type: string title: Agent summary: type: string title: Summary duration_ms: type: integer title: Duration Ms decision: anyOf: - $ref: '#/components/schemas/TimelineDecision' - type: 'null' has_artifact: type: boolean title: Has Artifact diff_vs_prev_attempt: anyOf: - type: string - type: 'null' title: Diff Vs Prev Attempt type: object required: - seq - ts - kind - agent - summary - duration_ms - decision - has_artifact - diff_vs_prev_attempt title: TimelineNode description: One node in the scrubbable investigation timeline. 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 default: 1800 type: object required: - access_token - refresh_token title: TokenResponse TopCostCase: properties: case_id: type: string title: Case Id runs: type: integer title: Runs total_cost_usd: type: number title: Total Cost Usd measured_call_count: type: integer title: Measured Call Count default: 0 estimated_cost_usd: type: number title: Estimated Cost Usd default: 0.0 estimated_call_count: type: integer title: Estimated Call Count default: 0 total_tokens: type: integer title: Total Tokens type: object required: - case_id - runs - total_cost_usd - total_tokens title: TopCostCase description: 'The most expensive cases over the window. Top-cost playbooks ≈ top-cost cases in the AiSOC data model: a case is the unit of work an analyst (or playbook) drives, and ``investigation_runs.case_id`` is the only stable join we can make between LLM cost and "what was this money spent on?".' TranslateRequest: properties: source_format: type: string enum: - sigma - spl - kql - esql - yara_l2 - udm title: Source Format description: Format of the input rule. source_rule: type: string minLength: 5 title: Source Rule description: The raw rule text to translate. target_formats: items: type: string enum: - sigma - spl - kql - esql - yara_l2 - udm type: array title: Target Formats description: Output formats to produce. default: - sigma - spl - kql - esql type: object required: - source_format - source_rule title: TranslateRequest TranslateResponse: properties: source_format: type: string enum: - sigma - spl - kql - esql - yara_l2 - udm title: Source Format results: items: $ref: '#/components/schemas/TranslationResult' type: array title: Results warnings: items: type: string type: array title: Warnings default: [] type: object required: - source_format - results title: TranslateResponse TranslatedQueryEnvelope: properties: esql: type: string title: Esql default: '' kql: type: string title: Kql default: '' spl: type: string title: Spl default: '' explanation: type: string title: Explanation default: '' type: object title: TranslatedQueryEnvelope description: 'Translator output stored alongside the NL question. Mirrors :class:`services.agents.app.nl_query.translator.TranslatedQuery` but without the ``intents`` IR (which is internal to the translator).' TranslationResult: properties: format: type: string enum: - sigma - spl - kql - esql - yara_l2 - udm title: Format label: type: string title: Label rule: type: string title: Rule notes: anyOf: - type: string - type: 'null' title: Notes type: object required: - format - label - rule title: TranslationResult TrendPoint: properties: timestamp: type: string title: Timestamp count: type: integer title: Count severity: type: string title: Severity type: object required: - timestamp - count - severity title: TrendPoint TroubleshootRequest: properties: connector_type: type: string maxLength: 100 minLength: 1 title: Connector Type error: type: string maxLength: 4000 minLength: 1 title: Error auth_config_keys: items: type: string type: array title: Auth Config Keys description: Names of fields the operator filled in. Values are intentionally *not* sent — the troubleshooter doesn't need plaintext credentials to diagnose '401 Unauthorized'. type: object required: - connector_type - error title: TroubleshootRequest description: AI-troubleshooter input from the wizard's failure UX. TroubleshootResponse: properties: likely_cause: type: string title: Likely Cause fix_steps: items: type: string type: array title: Fix Steps doc_link: anyOf: - type: string - type: 'null' title: Doc Link type: object required: - likely_cause - fix_steps title: TroubleshootResponse description: Structured guidance the wizard renders next to a failed test. TuningEntry: properties: rule_id: type: string title: Rule Id name: type: string title: Name description: anyOf: - type: string - type: 'null' title: Description category: type: string title: Category severity: type: string title: Severity status: type: string title: Status enabled: type: boolean title: Enabled confidence: type: integer title: Confidence fp_rate: type: number title: Fp Rate total_hits: type: integer title: Total Hits last_triggered_at: anyOf: - type: string - type: 'null' title: Last Triggered At tags: items: type: string type: array title: Tags mitre_tactics: items: type: string type: array title: Mitre Tactics mitre_techniques: items: type: string type: array title: Mitre Techniques version: type: integer title: Version updated_at: type: string title: Updated At suggestion: type: string enum: - disable - add_suppression - raise_threshold - tune_confidence - review_stale - healthy title: Suggestion score: type: integer title: Score reasons: items: type: string type: array title: Reasons auto_tune: type: boolean title: Auto Tune default: false dismissed_at: anyOf: - type: string - type: 'null' title: Dismissed At last_action: anyOf: - type: string - type: 'null' title: Last Action last_action_at: anyOf: - type: string - type: 'null' title: Last Action At type: object required: - rule_id - name - category - severity - status - enabled - confidence - fp_rate - total_hits - version - updated_at - suggestion - score title: TuningEntry description: One row in the tuning workbench — what an analyst sees. TuningFilters: properties: severity: anyOf: - type: string - type: 'null' title: Severity suggestion: anyOf: - type: string - type: 'null' title: Suggestion search: anyOf: - type: string - type: 'null' title: Search enabled_only: type: boolean title: Enabled Only default: false include_dismissed: type: boolean title: Include Dismissed default: false page: type: integer title: Page default: 1 page_size: type: integer title: Page Size default: 50 type: object title: TuningFilters description: Echo of the filters that built this response (handy for the UI). TuningResponse: properties: entries: items: $ref: '#/components/schemas/TuningEntry' type: array title: Entries summary: $ref: '#/components/schemas/TuningSummary' filters: $ref: '#/components/schemas/TuningFilters' total: type: integer title: Total generated_at: type: string title: Generated At type: object required: - entries - summary - filters - total - generated_at title: TuningResponse description: Wire shape for ``GET /detection/tuning``. TuningSummary: properties: total_rules: type: integer title: Total Rules actionable: type: integer title: Actionable healthy: type: integer title: Healthy disable_count: type: integer title: Disable Count default: 0 add_suppression_count: type: integer title: Add Suppression Count default: 0 raise_threshold_count: type: integer title: Raise Threshold Count default: 0 tune_confidence_count: type: integer title: Tune Confidence Count default: 0 review_stale_count: type: integer title: Review Stale Count default: 0 auto_tune_enabled: type: integer title: Auto Tune Enabled default: 0 average_fp_rate: type: number title: Average Fp Rate default: 0.0 high_fp_count: type: integer title: High Fp Count default: 0 type: object required: - total_rules - actionable - healthy title: TuningSummary description: Top-line counts for the workbench header. UpdateApiKeyRequest: properties: name: anyOf: - type: string maxLength: 255 minLength: 1 - type: 'null' title: Name scopes: anyOf: - items: type: string type: array - type: 'null' title: Scopes expires_in_days: anyOf: - type: integer maximum: 3650.0 minimum: 1.0 - type: 'null' title: Expires In Days description: Reset TTL from now; 0 = never type: object title: UpdateApiKeyRequest UpdateBody: properties: name: anyOf: - type: string - type: 'null' title: Name description: anyOf: - type: string - type: 'null' title: Description language: anyOf: - type: string - type: 'null' title: Language body: anyOf: - type: string - type: 'null' title: Body enabled: anyOf: - type: boolean - type: 'null' title: Enabled tags: anyOf: - items: type: string type: array - type: 'null' title: Tags mitre: anyOf: - items: type: string type: array - type: 'null' title: Mitre severity: anyOf: - type: string - type: 'null' title: Severity type: object title: UpdateBody UpdateCapabilitiesRequest: properties: allowed_capabilities: anyOf: - items: type: string type: array - type: 'null' title: Allowed Capabilities description: Capability strings (e.g. ['pull_alerts','query_logs']) the agent is permitted to invoke against this instance. None = no downscope. type: object title: UpdateCapabilitiesRequest description: 'Per-instance capability downscoping payload (Workstream 4). ``None`` removes the downscope ("agent may use every capability the connector class declares"). An explicit list — possibly empty — becomes the canonical allow-list. Validation against the connector class''s declared capabilities happens server-side so a tampered request can''t widen the agent''s reach beyond what the connector code actually implements.' UpdateCaseRequest: properties: title: anyOf: - type: string - type: 'null' title: Title description: anyOf: - type: string - type: 'null' title: Description severity: anyOf: - type: string enum: - info - low - medium - high - critical - type: 'null' title: Severity status: anyOf: - type: string enum: - new - triaged - investigating - contained - resolved - closed - open - in_progress - pending - pending_closure - cancelled - type: 'null' title: Status assignee: anyOf: - type: string - type: 'null' title: Assignee mitre_techniques: anyOf: - items: type: string type: array - type: 'null' title: Mitre Techniques compliance_frameworks: anyOf: - items: type: string type: array - type: 'null' title: Compliance Frameworks tags: anyOf: - additionalProperties: type: string type: object - type: 'null' title: Tags sla_due_at: anyOf: - type: string format: date-time - type: 'null' title: Sla Due At type: object title: UpdateCaseRequest UpdateChannel: type: string enum: - stable - beta - nightly - manual title: UpdateChannel UpdateConnectorRequest: properties: name: anyOf: - type: string - type: 'null' title: Name is_enabled: anyOf: - type: boolean - type: 'null' title: Is Enabled auth_config: anyOf: - additionalProperties: true type: object - type: 'null' title: Auth Config connector_config: anyOf: - additionalProperties: true type: object - type: 'null' title: Connector Config tags: anyOf: - items: type: string type: array - type: 'null' title: Tags type: object title: UpdateConnectorRequest UpdateHuntRequest: properties: title: anyOf: - type: string - type: 'null' title: Title hypothesis: anyOf: - type: string - type: 'null' title: Hypothesis mitre_tactic: anyOf: - type: string - type: 'null' title: Mitre Tactic mitre_technique: anyOf: - type: string - type: 'null' title: Mitre Technique status: anyOf: - type: string enum: - draft - active - completed - archived - type: 'null' title: Status priority: anyOf: - type: string enum: - low - medium - high - critical - type: 'null' title: Priority assigned_to: anyOf: - type: string - type: 'null' title: Assigned To query_esql: anyOf: - type: string - type: 'null' title: Query Esql query_spl: anyOf: - type: string - type: 'null' title: Query Spl query_kql: anyOf: - type: string - type: 'null' title: Query Kql tags: anyOf: - items: type: string type: array - type: 'null' title: Tags type: object title: UpdateHuntRequest UpdateObservablesRequest: properties: nodes: items: $ref: '#/components/schemas/ObservableNode' type: array title: Nodes edges: items: $ref: '#/components/schemas/ObservableEdge' type: array title: Edges type: object title: UpdateObservablesRequest UpdateSavedViewRequest: properties: name: anyOf: - type: string maxLength: 120 minLength: 1 - type: 'null' title: Name filters: anyOf: - additionalProperties: true type: object - type: 'null' title: Filters columns: anyOf: - items: {} type: array - additionalProperties: true type: object - type: 'null' title: Columns is_default: anyOf: - type: boolean - type: 'null' title: Is Default additionalProperties: false type: object title: UpdateSavedViewRequest description: 'Partial update — every field is optional. Sending an empty body is a no-op (returns the row unchanged). To *clear* ``columns`` and fall back to the page default column set, the caller passes ``columns: null`` explicitly — the optional / null distinction is preserved in the JSON.' UpdateTaskRequest: properties: title: anyOf: - type: string - type: 'null' title: Title status: anyOf: - type: string enum: - todo - in_progress - done - type: 'null' title: Status assignee: anyOf: - type: string - type: 'null' title: Assignee dueAt: anyOf: - type: string format: date-time - type: 'null' title: Dueat type: object title: UpdateTaskRequest UpdateTenantSettingsRequest: properties: settings: additionalProperties: true type: object title: Settings default: {} type: object title: UpdateTenantSettingsRequest UpdateUserRequest: properties: username: anyOf: - type: string - type: 'null' title: Username role: anyOf: - type: string - type: 'null' title: Role is_active: anyOf: - type: boolean - type: 'null' title: Is Active reason: anyOf: - type: string - type: 'null' title: Reason type: object title: UpdateUserRequest UpsertAlertGraphRequest: properties: alert_id: type: string title: Alert Id title: type: string title: Title severity: type: string title: Severity mitre_techniques: items: type: string type: array title: Mitre Techniques host_id: anyOf: - type: string - type: 'null' title: Host Id user_id: anyOf: - type: string - type: 'null' title: User Id ioc_values: items: type: string type: array title: Ioc Values type: object required: - alert_id - title - severity title: UpsertAlertGraphRequest UpsertCaseGraphRequest: properties: case_id: type: string title: Case Id title: type: string title: Title severity: type: string title: Severity alert_ids: items: type: string type: array title: Alert Ids type: object required: - case_id - title - severity title: UpsertCaseGraphRequest UpsertHostRequest: properties: host_id: type: string title: Host Id hostname: type: string title: Hostname ip_address: type: string title: Ip Address default: '' os: type: string title: Os default: '' criticality: type: string title: Criticality default: medium type: object required: - host_id - hostname title: UpsertHostRequest UpsertUserRequest: properties: user_id: type: string title: User Id username: type: string title: Username email: type: string title: Email default: '' department: type: string title: Department default: '' risk_score: type: number title: Risk Score default: 0.0 type: object required: - user_id - username title: UpsertUserRequest UserMeResponse: properties: id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id email: type: string title: Email username: type: string title: Username role: type: string title: Role is_active: type: boolean title: Is Active preferences: additionalProperties: true type: object title: Preferences default: {} type: object required: - id - tenant_id - email - username - role - is_active title: UserMeResponse UserPage: properties: items: items: $ref: '#/components/schemas/AdminUserOut' 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: UserPage UserPatch: properties: is_active: type: boolean title: Is Active reason: anyOf: - type: string maxLength: 500 - type: 'null' title: Reason type: object required: - is_active title: UserPatch description: 'Body for PATCH /admin/users/{id} — status changes only. Roles deliberately do not live on this body: a status patch that can also carry roles turns every future "toggle" feature into a privilege escalation vector. Roles move through the explicit role routes.' UserResponse: properties: id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id email: type: string title: Email username: type: string title: Username role: type: string title: Role is_active: type: boolean title: Is Active last_login: anyOf: - type: string format: date-time - type: 'null' title: Last Login created_at: type: string format: date-time title: Created At type: object required: - id - tenant_id - email - username - role - is_active - last_login - created_at title: UserResponse UserRoleAssignment: properties: user_id: type: string format: uuid title: User Id role_id: type: string format: uuid title: Role Id reason: anyOf: - type: string maxLength: 500 - type: 'null' title: Reason type: object required: - user_id - role_id title: UserRoleAssignment UserRoleOut: properties: user_id: type: string format: uuid title: User Id role_id: type: string format: uuid title: Role Id role_name: type: string title: Role Name type: object required: - user_id - role_id - role_name title: UserRoleOut ValidateResponse: properties: valid: type: boolean title: Valid skill_id: anyOf: - type: string - type: 'null' title: Skill Id name: anyOf: - type: string - type: 'null' title: Name expected_pivots: items: type: string type: array title: Expected Pivots available_tools: $ref: '#/components/schemas/AvailableTools' error: anyOf: - type: string - type: 'null' title: Error type: object required: - valid title: ValidateResponse ValidationError: properties: loc: items: anyOf: - type: string - type: integer type: array title: Location msg: type: string title: Message type: type: string title: Error Type input: title: Input ctx: type: object title: Context type: object required: - loc - msg - type title: ValidationError VendorReadRequest: properties: capability: type: string title: Capability target: type: string maxLength: 512 minLength: 1 title: Target vendor: type: string maxLength: 64 title: Vendor default: '' params: additionalProperties: true type: object title: Params type: object required: - capability - target title: VendorReadRequest description: 'One read-only verb against one entity. ``params`` is filtered against a per-verb allowlist before it travels, because by the time it reaches the actions service it is the same dictionary that carries the decrypted credential. An unfiltered pass-through would let a caller supply their own vendor credential and have the read run somewhere else entirely.' VerificationPolicyIn: properties: alert_class: type: string maxLength: 200 minLength: 1 title: Alert Class description: An alert's rule_id, category or alert_class. Matched case-insensitively. type: object required: - alert_class title: VerificationPolicyIn VerificationPolicyOut: properties: alert_class: type: string title: Alert Class enabled_by: anyOf: - type: string format: uuid - type: 'null' title: Enabled By enabled_at: type: string format: date-time title: Enabled At type: object required: - alert_class - enabled_by - enabled_at title: VerificationPolicyOut VerifyRequest: properties: mfa_token: type: string title: Mfa Token code: type: string maxLength: 64 minLength: 1 title: Code type: object required: - mfa_token - code title: VerifyRequest VulnerabilityCreate: properties: asset_id: type: string format: uuid title: Asset Id cve_id: anyOf: - type: string - type: 'null' title: Cve Id title: type: string title: Title description: anyOf: - type: string - type: 'null' title: Description severity: type: string title: Severity default: medium cvss_score: anyOf: - type: number - type: 'null' title: Cvss Score cvss_vector: anyOf: - type: string - type: 'null' title: Cvss Vector epss_score: anyOf: - type: number - type: 'null' title: Epss Score is_exploited: type: boolean title: Is Exploited default: false source: type: string title: Source external_id: anyOf: - type: string - type: 'null' title: External Id metadata: additionalProperties: true type: object title: Metadata type: object required: - asset_id - title - source title: VulnerabilityCreate VulnerabilityOut: properties: asset_id: type: string format: uuid title: Asset Id cve_id: anyOf: - type: string - type: 'null' title: Cve Id title: type: string title: Title description: anyOf: - type: string - type: 'null' title: Description severity: type: string title: Severity default: medium cvss_score: anyOf: - type: number - type: 'null' title: Cvss Score cvss_vector: anyOf: - type: string - type: 'null' title: Cvss Vector epss_score: anyOf: - type: number - type: 'null' title: Epss Score is_exploited: type: boolean title: Is Exploited default: false source: type: string title: Source external_id: anyOf: - type: string - type: 'null' title: External Id metadata: additionalProperties: true type: object title: Metadata id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id first_found: type: string title: First Found last_found: type: string title: Last Found remediated_at: anyOf: - type: string - type: 'null' title: Remediated At asset_metadata: additionalProperties: true type: object title: Asset Metadata type: object required: - asset_id - title - source - id - tenant_id - first_found - last_found - remediated_at title: VulnerabilityOut WaitlistEntriesResponse: properties: entries: items: $ref: '#/components/schemas/WaitlistEntryWire' type: array title: Entries total: type: integer title: Total type: object required: - entries - total title: WaitlistEntriesResponse WaitlistEntryWire: properties: id: type: string title: Id email: type: string title: Email company: type: string title: Company role: type: string title: Role soc_stack: items: type: string type: array title: Soc Stack motivation: type: string title: Motivation status: type: string title: Status provisioned_tenant_id: anyOf: - type: string - type: 'null' title: Provisioned Tenant Id created_at: type: string title: Created At contacted_at: anyOf: - type: string - type: 'null' title: Contacted At onboarded_at: anyOf: - type: string - type: 'null' title: Onboarded At type: object required: - id - email - company - role - soc_stack - motivation - status - provisioned_tenant_id - created_at - contacted_at - onboarded_at title: WaitlistEntryWire description: Admin-list / admin-patch response shape. WaitlistPatchRequest: properties: status: type: string maxLength: 32 minLength: 1 title: Status type: object required: - status title: WaitlistPatchRequest WaitlistSignupRequest: properties: email: type: string maxLength: 320 minLength: 3 title: Email company: type: string maxLength: 255 minLength: 1 title: Company role: type: string maxLength: 100 minLength: 1 title: Role soc_stack: items: type: string type: array title: Soc Stack motivation: type: string maxLength: 4000 minLength: 1 title: Motivation type: object required: - email - company - role - motivation title: WaitlistSignupRequest description: The exact payload the marketing form posts. WaitlistSignupResponse: properties: ok: type: boolean title: Ok default: true message: type: string title: Message default: We'll be in touch within 5 business days. entry_id: anyOf: - type: string - type: 'null' title: Entry Id type: object title: WaitlistSignupResponse description: 'The reply to a successful (or idempotent-no-op) signup. Same shape whether the email was net-new or already on the list — the prospect should not be able to enumerate existing signups.' WarRoomModel: properties: id: type: string title: Id case_id: type: string title: Case Id provider: type: string title: Provider channel_id: type: string title: Channel Id channel_name: type: string title: Channel Name channel_url: type: string title: Channel Url state: type: string title: State opened_by: type: string title: Opened By last_synced_at: anyOf: - type: string - type: 'null' title: Last Synced At last_error: type: string title: Last Error type: object required: - id - case_id - provider - channel_id - channel_name - channel_url - state - opened_by - last_synced_at - last_error title: WarRoomModel WatchlistUpdate: properties: is_watchlisted: type: boolean title: Is Watchlisted watchlist_reason: anyOf: - type: string - type: 'null' title: Watchlist Reason type: object required: - is_watchlisted title: WatchlistUpdate WebAuthnChallengeRequest: properties: mfa_token: type: string title: Mfa Token type: object required: - mfa_token title: WebAuthnChallengeRequest WebAuthnVerifyRequest: properties: mfa_token: type: string title: Mfa Token challenge: type: string title: Challenge credential: additionalProperties: true type: object title: Credential type: object required: - mfa_token - challenge - credential title: WebAuthnVerifyRequest WebhookCreate: properties: name: type: string maxLength: 255 title: Name url: type: string maxLength: 2048 title: Url event_types: items: type: string type: array maxItems: 64 title: Event Types type: object required: - name - url title: WebhookCreate WebhookOut: properties: id: type: string format: uuid title: Id name: type: string title: Name url: type: string title: Url event_types: items: type: string type: array title: Event Types enabled: type: boolean title: Enabled has_secret: type: boolean title: Has Secret created_at: type: string format: date-time title: Created At type: object required: - id - name - url - event_types - enabled - has_secret - created_at title: WebhookOut description: 'The public projection. ``secret`` is never in it. Not even decrypted, and not even to the operator who set it: rotation issues a new secret rather than showing the old one, the same rule the connector wizard follows. ``has_secret`` answers the only question a console needs.' WebhookUpdate: properties: name: anyOf: - type: string maxLength: 255 - type: 'null' title: Name url: anyOf: - type: string maxLength: 2048 - type: 'null' title: Url event_types: anyOf: - items: type: string type: array maxItems: 64 - type: 'null' title: Event Types enabled: anyOf: - type: boolean - type: 'null' title: Enabled type: object title: WebhookUpdate WhitelistCreate: properties: action_type: type: string title: Action Type blast_radius: type: string title: Blast Radius constraints: additionalProperties: true type: object title: Constraints expires_at: anyOf: - type: string format: date-time - type: 'null' title: Expires At type: object required: - action_type - blast_radius title: WhitelistCreate WhitelistOut: properties: action_type: type: string title: Action Type blast_radius: type: string title: Blast Radius constraints: additionalProperties: true type: object title: Constraints expires_at: anyOf: - type: string format: date-time - type: 'null' title: Expires At id: type: string format: uuid title: Id tenant_id: type: string format: uuid title: Tenant Id approved_by: anyOf: - type: string format: uuid - type: 'null' title: Approved By created_at: type: string title: Created At type: object required: - action_type - blast_radius - id - tenant_id - approved_by - created_at title: WhitelistOut WorkloadCreated: properties: identity: additionalProperties: true type: object title: Identity secret: type: string title: Secret warning: type: string title: Warning default: Store this now. It is shown once and is not recoverable. type: object required: - identity - secret title: WorkloadCreated description: The one response that carries secret material, and only on create. WorkloadIn: properties: service: type: string title: Service description: anyOf: - type: string maxLength: 500 - type: 'null' title: Description scopes: items: type: string type: array maxItems: 40 title: Scopes expires_in_days: anyOf: - type: integer maximum: 3650.0 minimum: 1.0 - type: 'null' title: Expires In Days type: object required: - service title: WorkloadIn app__api__v1__endpoints__business_context__UpdateRuleRequest: properties: yaml: type: string maxLength: 262144 title: Yaml type: object required: - yaml title: UpdateRuleRequest app__api__v1__endpoints__cases__TimelineEvent: properties: id: type: string title: Id type: type: string title: Type timestamp: type: string format: date-time title: Timestamp title: type: string title: Title description: anyOf: - type: string - type: 'null' title: Description actor: anyOf: - type: string - type: 'null' title: Actor type: object required: - id - type - timestamp - title title: TimelineEvent app__api__v1__endpoints__cases__TimelineResponse: properties: events: items: $ref: '#/components/schemas/app__api__v1__endpoints__cases__TimelineEvent' type: array title: Events type: object required: - events title: TimelineResponse app__api__v1__endpoints__detection_rules__HuntResponse: properties: hunt_id: type: string title: Hunt Id rules_evaluated: type: integer title: Rules Evaluated rules_matched: type: integer title: Rules Matched total_events_scanned: type: integer title: Total Events Scanned matched_events: items: additionalProperties: true type: object type: array title: Matched Events match_summary: items: additionalProperties: true type: object type: array title: Match Summary execution_time_ms: type: number title: Execution Time Ms errors: items: type: string type: array title: Errors type: object required: - hunt_id - rules_evaluated - rules_matched - total_events_scanned - matched_events - match_summary - execution_time_ms - errors title: HuntResponse app__api__v1__endpoints__detection_rules__UpdateRuleRequest: properties: name: anyOf: - type: string - type: 'null' title: Name description: anyOf: - type: string - type: 'null' title: Description rule_body: anyOf: - type: string - type: 'null' title: Rule Body status: anyOf: - type: string - type: 'null' title: Status severity: anyOf: - type: string - type: 'null' title: Severity confidence: anyOf: - type: integer - type: 'null' title: Confidence tags: anyOf: - items: type: string type: array - type: 'null' title: Tags type: object title: UpdateRuleRequest app__api__v1__endpoints__hunts__HuntResponse: properties: id: type: string format: uuid title: Id title: type: string title: Title hypothesis: type: string title: Hypothesis mitre_tactic: anyOf: - type: string - type: 'null' title: Mitre Tactic mitre_technique: anyOf: - type: string - type: 'null' title: Mitre Technique status: type: string title: Status priority: type: string title: Priority query_esql: anyOf: - type: string - type: 'null' title: Query Esql query_spl: anyOf: - type: string - type: 'null' title: Query Spl query_kql: anyOf: - type: string - type: 'null' title: Query Kql findings: items: additionalProperties: true type: object type: array title: Findings false_positive_rate: anyOf: - type: number - type: 'null' title: False Positive Rate assigned_to: anyOf: - type: string - type: 'null' title: Assigned To tags: items: type: string type: array title: Tags created_at: type: string format: date-time title: Created At updated_at: type: string format: date-time title: Updated At completed_at: anyOf: - type: string format: date-time - type: 'null' title: Completed At created_by: anyOf: - type: string - type: 'null' title: Created By type: object required: - id - title - hypothesis - mitre_tactic - mitre_technique - status - priority - query_esql - query_spl - query_kql - findings - false_positive_rate - assigned_to - tags - created_at - updated_at - completed_at - created_by title: HuntResponse app__api__v1__endpoints__identity_timeline__TimelineEvent: properties: event_id: type: string format: uuid title: Event Id timestamp: type: string format: date-time title: Timestamp event_type: type: string enum: - alert - login - process - network - file - other title: Event Type source: type: string title: Source description: type: string title: Description severity: anyOf: - type: string - type: 'null' title: Severity mitre_technique: anyOf: - type: string - type: 'null' title: Mitre Technique raw: anyOf: - additionalProperties: true type: object - type: 'null' title: Raw type: object required: - event_id - timestamp - event_type - source - description title: TimelineEvent app__api__v1__endpoints__investigations__TimelineResponse: properties: run_id: type: string format: uuid title: Run Id case_id: type: string title: Case Id status: type: string title: Status total_duration_ms: type: integer title: Total Duration Ms attempt_count: type: integer title: Attempt Count nodes: items: $ref: '#/components/schemas/TimelineNode' type: array title: Nodes type: object required: - run_id - case_id - status - total_duration_ms - attempt_count - nodes title: TimelineResponse description: 'Full timeline for a closed or in-progress run. ``nodes`` are ordered by ``seq`` ascending. ``total_duration_ms`` is the wall-clock span from first to last event. ``attempt_count`` counts unique retry attempts inferred from ``agent_start`` events.' securitySchemes: HTTPBearer: type: http scheme: bearer