openapi: 3.1.0
info:
  title: Superagent REST API
  version: 1.0.0
  description: |
    Create security reports, retrieve and manage findings, assess contributor
    trust, and start automated finding triage. Organization APIs use an API key;
    agent monitoring clients pair once and then use a revocable client credential.
  contact:
    url: https://superagent.sh
servers:
  - url: https://superagent.sh/api/v1
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Findings
    description: Retrieve, update, delete, and triage security findings.
  - name: Reports
    description: Start repository and Web app security reports.
  - name: Contributor Trust
    description: Retrieve cached contributor trust and start asynchronous scans.
  - name: Agent monitoring
    description: Pair endpoint clients, sync security rules, and ingest detections.
paths:
  /findings:
    get:
      operationId: listFindings
      summary: List findings
      description: Returns paginated finding summaries for the API key's organization.
      tags:
        - Findings
      parameters:
        - name: limit
          in: query
          description: Number of results to return.
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
        - name: offset
          in: query
          description: Number of matching results to skip.
          schema:
            type: integer
            minimum: 0
            maximum: 2147483647
            default: 0
        - name: kind
          in: query
          description: Restrict results to one finding type.
          schema:
            $ref: "#/components/schemas/FindingKind"
        - name: triage_status
          in: query
          description: Restrict results to one triage state.
          schema:
            $ref: "#/components/schemas/TriageStatus"
      responses:
        "200":
          description: A page of finding summaries.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FindingListResponse"
              example:
                data:
                  - id: finding_uuid
                    object: finding
                    kind: repository_red_team
                    report_id: report_uuid
                    title: SQL injection in search endpoint
                    description: User input is interpolated into a SQL query.
                    cvss_vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
                    severity: null
                    risk_level: high
                    weakness: CWE-89
                    cwe_ids:
                      - CWE-89
                    advisory_url: null
                    source: superagent
                    status: in_review
                    triage_status: new
                    triage_resolution: null
                    board_position: 1000
                    report:
                      repository: https://github.com/acme/web
                      label: acme/web
                      status: in_review
                    created_at: "2026-07-22T07:00:00.000Z"
                    updated_at: "2026-07-22T07:00:00.000Z"
                pagination:
                  limit: 25
                  offset: 0
                  total: 1
                  has_more: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"
  /findings/{finding_id}:
    parameters:
      - $ref: "#/components/parameters/FindingId"
    get:
      operationId: getFinding
      summary: Retrieve a finding
      description: Returns full triage, remediation, report, and pull-request context.
      tags:
        - Findings
      responses:
        "200":
          description: The finding.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FindingResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    patch:
      operationId: updateFinding
      summary: Update a finding
      description: Updates the finding's manual triage lifecycle state.
      tags:
        - Findings
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateFindingRequest"
            example:
              triage_status: resolved
              triage_resolution: fixed
              board_position: 1000
      responses:
        "200":
          description: The updated finding.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FindingResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
    delete:
      operationId: deleteFinding
      summary: Delete a finding
      description: Permanently deletes a finding. This action cannot be undone.
      tags:
        - Findings
      responses:
        "204":
          description: The finding was deleted.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
  /findings/{finding_id}/triage:
    parameters:
      - $ref: "#/components/parameters/FindingId"
    post:
      operationId: triggerFindingTriage
      summary: Trigger finding triage
      description: Starts billable automated triage for a finding.
      tags:
        - Findings
      responses:
        "202":
          description: Triage was accepted and queued.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TriageResponse"
              example:
                data:
                  id: finding_uuid
                  object: finding
                  kind: repository_red_team
                  triage_status: triaging
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "409":
          $ref: "#/components/responses/Conflict"
        "500":
          $ref: "#/components/responses/InternalError"
  /reports:
    get:
      operationId: listReports
      summary: List reports
      description: |
        Returns paginated repository and Web app reports for the API key's
        organization, newest first.
      tags:
        - Reports
      parameters:
        - name: limit
          in: query
          description: Number of results to return.
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
        - name: offset
          in: query
          description: Number of matching results to skip.
          schema:
            type: integer
            minimum: 0
            maximum: 2147483647
            default: 0
        - name: type
          in: query
          description: Restrict results to one report type.
          schema:
            $ref: "#/components/schemas/ReportType"
        - name: status
          in: query
          description: Restrict results to one report status.
          schema:
            $ref: "#/components/schemas/ReportStatus"
      responses:
        "200":
          description: A page of reports.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReportListResponse"
              example:
                data:
                  - id: report_uuid
                    object: report
                    type: repository
                    repository: https://github.com/acme/web
                    custom_goal_prompt: null
                    status: done
                    sandbox_status: ready
                    agent_status: completed
                    created_at: "2026-07-22T07:00:00.000Z"
                    updated_at: "2026-07-22T09:00:00.000Z"
                pagination:
                  limit: 25
                  offset: 0
                  total: 1
                  has_more: false
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"
  /reports/{report_id}:
    parameters:
      - $ref: "#/components/parameters/ReportId"
    get:
      operationId: getReport
      summary: Retrieve a report
      description: Returns a repository or Web app report by identifier.
      tags:
        - Reports
      responses:
        "200":
          description: The report.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReportResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
  /reports/repository:
    post:
      operationId: createRepositoryReport
      summary: Create a repository report
      description: |
        Starts a report for a GitHub repository connected to the API key's
        organization. Sandbox provisioning continues asynchronously.
      tags:
        - Reports
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateRepositoryReportRequest"
            example:
              repository: acme/web
              custom_goal_prompt: Focus on authorization boundaries.
      responses:
        "202":
          description: The report was created and accepted for provisioning.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RepositoryReportResponse"
              example:
                data:
                  id: report_uuid
                  object: report
                  type: repository
                  repository: https://github.com/acme/web
                  custom_goal_prompt: Focus on authorization boundaries.
                  status: in_progress
                  sandbox_status: provisioning
                  agent_status: pending
                  created_at: "2026-07-22T07:00:00.000Z"
                  updated_at: "2026-07-22T07:00:00.000Z"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
  /reports/agent:
    post:
      operationId: createAgentReport
      summary: Create an Agent report
      description: |
        Starts adversarial testing against an AI agent exposed through a Web app
        or API endpoint. Sandbox provisioning continues asynchronously.
        Credentials and headers are never returned.
      tags:
        - Reports
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateAgentReportRequest"
            example:
              target_type: api
              target_url: https://api.example.com/v1/chat
              agent_type: chatbot
              headers:
                Authorization: Bearer target_token
              custom_goal_prompt: |
                POST JSON with a messages array and keep the model fixed.
      responses:
        "202":
          description: The report was created and accepted for provisioning.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentReportResponse"
              example:
                data:
                  id: report_uuid
                  object: report
                  type: agent
                  description: api.example.com
                  agent_type: chatbot
                  target_type: api
                  target_url: https://api.example.com/v1/chat
                  allowed_host: api.example.com
                  request_throttle_rpm: null
                  custom_goal_prompt: POST JSON with a messages array.
                  status: in_progress
                  sandbox_status: provisioning
                  agent_status: pending
                  created_at: "2026-07-31T12:00:00.000Z"
                  updated_at: "2026-07-31T12:00:00.000Z"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"
  /reports/web-app:
    post:
      operationId: createWebAppReport
      summary: Create a Web app report
      description: |
        Starts a report for a public HTTP or HTTPS target. Sandbox provisioning
        continues asynchronously. Credentials and headers are never returned.
      tags:
        - Reports
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateWebAppReportRequest"
            example:
              target_url: https://staging.example.com/account
              login_email: security-test@example.com
              login_password: secret
              browser_headers:
                X-Test-Environment: security
              request_throttle_rpm: 120
              custom_goal_prompt: Focus on account authorization.
      responses:
        "202":
          description: The report was created and accepted for provisioning.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebAppReportResponse"
              example:
                data:
                  id: report_uuid
                  object: report
                  type: web_app
                  target_url: https://staging.example.com/account
                  allowed_host: staging.example.com
                  request_throttle_rpm: 120
                  custom_goal_prompt: Focus on account authorization.
                  status: in_progress
                  sandbox_status: provisioning
                  agent_status: pending
                  created_at: "2026-07-22T07:00:00.000Z"
                  updated_at: "2026-07-22T07:00:00.000Z"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "500":
          $ref: "#/components/responses/InternalError"
  /contributors/{login}/trust:
    parameters:
      - $ref: "#/components/parameters/ContributorLogin"
    get:
      operationId: getContributorTrust
      summary: Retrieve contributor trust
      description: |
        Returns the latest globally cached Contributor Trust result for a GitHub
        login. Returns `404` when no cached result exists.
      tags:
        - Contributor Trust
      responses:
        "200":
          description: The latest cached Contributor Trust result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContributorTrustResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
  /contributors/{login}/trust-scans:
    parameters:
      - $ref: "#/components/parameters/ContributorLogin"
    post:
      operationId: createContributorTrustScan
      summary: Start a contributor trust scan
      description: |
        Starts an asynchronous scan for a GitHub login. The API key's
        organization must have an active Superagent GitHub App installation and
        at least one enabled registered webhook subscribed to
        `contributor_trust.finished`. Returns `409` when either prerequisite is
        missing or a scan for the login is already active.
      tags:
        - Contributor Trust
      responses:
        "202":
          description: The Contributor Trust scan was accepted and queued.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContributorTrustScanResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "409":
          $ref: "#/components/responses/Conflict"
        "500":
          $ref: "#/components/responses/InternalError"
  /contributor-trust-scans/{scan_id}:
    parameters:
      - $ref: "#/components/parameters/ContributorTrustScanId"
    get:
      operationId: getContributorTrustScan
      summary: Retrieve a contributor trust scan
      description: |
        Returns the status and result of a scan started by the API key's
        organization. Missing scans and scans owned by another organization
        return `404`.
      tags:
        - Contributor Trust
      responses:
        "200":
          description: The organization-scoped Contributor Trust scan.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ContributorTrustScanResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
        "500":
          $ref: "#/components/responses/InternalError"
  /agents/clients:
    get:
      operationId: listAgentClients
      summary: List agent clients
      tags: [Agent monitoring]
      parameters:
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 25 } }
        - { name: offset, in: query, schema: { type: integer, minimum: 0, default: 0 } }
      responses:
        "200":
          description: Registered endpoint clients.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentClientListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      operationId: createAgentClient
      summary: Register an agent client
      tags: [Agent monitoring]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name: { type: string, minLength: 1, maxLength: 100 }
      responses:
        "201":
          description: Client and one-time pairing token.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentClientPairingResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "409":
          $ref: "#/components/responses/Conflict"
  /agents/clients/{client_id}:
    parameters:
      - { name: client_id, in: path, required: true, schema: { type: string, format: uuid } }
    get:
      operationId: getAgentClient
      summary: Retrieve an agent client
      tags: [Agent monitoring]
      responses:
        "200":
          description: Registered endpoint client.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentClientResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      operationId: updateAgentClient
      summary: Rename an agent client
      tags: [Agent monitoring]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name: { type: string, minLength: 1, maxLength: 100 }
      responses:
        "200":
          description: Updated client.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentClientResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      operationId: revokeAgentClient
      summary: Revoke an agent client
      tags: [Agent monitoring]
      responses:
        "200":
          description: Client and credentials revoked.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
  /agents/clients/{client_id}/pairing-token:
    parameters:
      - { name: client_id, in: path, required: true, schema: { type: string, format: uuid } }
    post:
      operationId: createAgentClientPairingToken
      summary: Create a pairing token
      tags: [Agent monitoring]
      responses:
        "201":
          description: New short-lived pairing token.
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
  /agents/clients/{client_id}/groups:
    parameters:
      - { name: client_id, in: path, required: true, schema: { type: string, format: uuid } }
    put:
      operationId: setAgentClientGroups
      summary: Replace client group memberships
      tags: [Agent monitoring]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [group_ids]
              properties:
                group_ids:
                  type: array
                  items: { type: string, format: uuid }
      responses:
        "200":
          description: Updated client.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentClientResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /agents/groups:
    get:
      operationId: listAgentGroups
      summary: List agent groups
      tags: [Agent monitoring]
      parameters:
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 25 } }
        - { name: offset, in: query, schema: { type: integer, minimum: 0, default: 0 } }
      responses:
        "200":
          description: Client groups.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentGroupListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      operationId: createAgentGroup
      summary: Create an agent group
      tags: [Agent monitoring]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name: { type: string, minLength: 1, maxLength: 100 }
                description: { type: [string, "null"], maxLength: 500 }
                client_ids:
                  type: array
                  items: { type: string, format: uuid }
      responses:
        "201":
          description: Created group.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentGroupResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /agents/groups/{group_id}:
    parameters:
      - { name: group_id, in: path, required: true, schema: { type: string, format: uuid } }
    get:
      operationId: getAgentGroup
      summary: Retrieve an agent group
      tags: [Agent monitoring]
      responses:
        "200":
          description: Client group.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentGroupResponse"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      operationId: updateAgentGroup
      summary: Update an agent group
      tags: [Agent monitoring]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name: { type: string, minLength: 1, maxLength: 100 }
                description: { type: [string, "null"], maxLength: 500 }
      responses:
        "200":
          description: Updated group.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentGroupResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
    delete:
      operationId: deleteAgentGroup
      summary: Delete an agent group
      tags: [Agent monitoring]
      responses:
        "200":
          description: Group deleted.
        "404":
          $ref: "#/components/responses/NotFound"
  /agents/groups/{group_id}/rules:
    parameters:
      - { name: group_id, in: path, required: true, schema: { type: string, format: uuid } }
    put:
      operationId: setAgentGroupRules
      summary: Replace group rule assignments
      tags: [Agent monitoring]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [rule_ids]
              properties:
                rule_ids:
                  type: array
                  items: { type: string, format: uuid }
      responses:
        "200":
          description: Updated group.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentGroupResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
  /agents/groups/{group_id}/builtin-rules:
    parameters:
      - { name: group_id, in: path, required: true, schema: { type: string, format: uuid } }
    put:
      operationId: setAgentGroupBuiltinRules
      summary: Replace group built-in rule exclusions
      tags: [Agent monitoring]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [excluded_builtin_rule_ids]
              properties:
                excluded_builtin_rule_ids:
                  type: array
                  items: { type: string }
      responses:
        "200":
          description: Updated group.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentGroupResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "404":
          $ref: "#/components/responses/NotFound"
  /agents/rules:
    get:
      operationId: listAgentRules
      summary: List agent rules
      tags: [Agent monitoring]
      parameters:
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 25 } }
        - { name: offset, in: query, schema: { type: integer, minimum: 0, default: 0 } }
      responses:
        "200":
          description: Security rules.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentRuleListResponse"
    post:
      operationId: createAgentRule
      summary: Create an agent rule
      tags: [Agent monitoring]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AgentRuleWriteRequest"
      responses:
        "201":
          description: Created rule.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentRuleResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "409":
          $ref: "#/components/responses/Conflict"
  /agents/rules/builtin:
    get:
      operationId: listAgentBuiltinRules
      summary: List pinned built-in agent rules
      tags: [Agent monitoring]
      parameters:
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 25 } }
        - { name: offset, in: query, schema: { type: integer, minimum: 0, default: 0 } }
        - { name: category, in: query, schema: { type: string } }
        - { name: severity, in: query, schema: { type: string, enum: [info, low, medium, high, critical] } }
      responses:
        "200":
          description: Built-in Numbat rules and organization-effective modes.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentBuiltinRuleListResponse"
  /agents/rules/builtin/{rule_id}:
    parameters:
      - { name: rule_id, in: path, required: true, schema: { type: string } }
    get:
      operationId: getAgentBuiltinRule
      summary: Retrieve a built-in agent rule
      tags: [Agent monitoring]
      responses:
        "200":
          description: Built-in rule and effective organization override.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentBuiltinRuleResponse"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      operationId: setAgentBuiltinRuleMode
      summary: Set a built-in agent rule mode
      tags: [Agent monitoring]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [mode]
              properties:
                mode:
                  type: string
                  enum: [monitor, enforce, disabled]
      responses:
        "200":
          description: Updated effective built-in rule.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentBuiltinRuleResponse"
        "422":
          $ref: "#/components/responses/BadRequest"
    delete:
      operationId: restoreAgentBuiltinRule
      summary: Restore a built-in agent rule
      description: Deletes the organization override and restores shipped YAML and mode.
      tags: [Agent monitoring]
      responses:
        "200":
          description: Restored built-in rule.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentBuiltinRuleResponse"
  /agents/rules/validate:
    post:
      operationId: validateAgentRule
      summary: Validate agent rule YAML
      description: Compiles YAML with the authoritative Numbat validator without saving it.
      tags: [Agent monitoring]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [yaml_text]
              properties:
                yaml_text: { type: string }
      responses:
        "200":
          description: Valid rule metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentRuleValidationResponse"
        "422":
          $ref: "#/components/responses/BadRequest"
        "503":
          $ref: "#/components/responses/InternalError"
  /agents/rules/generate:
    post:
      operationId: generateAgentRule
      summary: Generate validated agent rule YAML
      description: Uses Kimi K3 and authoritative Numbat validation. The result is not saved.
      tags: [Agent monitoring]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AgentRuleGenerationRequest"
      responses:
        "200":
          description: Generated and validated YAML.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentRuleGenerationResponse"
        "422":
          $ref: "#/components/responses/BadRequest"
        "503":
          $ref: "#/components/responses/InternalError"
  /agents/rules/{rule_id}:
    parameters:
      - { name: rule_id, in: path, required: true, schema: { type: string, format: uuid } }
    get:
      operationId: getAgentRule
      summary: Retrieve an agent rule
      tags: [Agent monitoring]
      responses:
        "200":
          description: Security rule.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentRuleResponse"
        "404":
          $ref: "#/components/responses/NotFound"
    patch:
      operationId: updateAgentRule
      summary: Update an agent rule
      tags: [Agent monitoring]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AgentRuleWriteRequest"
      responses:
        "200":
          description: Updated rule.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentRuleResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
    delete:
      operationId: deleteAgentRule
      summary: Delete an agent rule
      tags: [Agent monitoring]
      responses:
        "200":
          description: Rule deleted.
        "404":
          $ref: "#/components/responses/NotFound"
  /agents/pair:
    post:
      operationId: pairAgentClient
      summary: Pair an agent client
      tags:
        - Agent monitoring
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [clientId, pairingToken]
              properties:
                clientId:
                  type: string
                  format: uuid
                pairingToken:
                  type: string
                platform:
                  type: string
                architecture:
                  type: string
                cliVersion:
                  type: string
                engineVersion:
                  type: string
      responses:
        "200":
          description: A revocable client credential. The pairing token is consumed.
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "409":
          $ref: "#/components/responses/Conflict"
        "429":
          description: Too many pairing attempts.
  /agents/config:
    get:
      operationId: getAgentClientConfig
      summary: Retrieve effective security rules
      tags:
        - Agent monitoring
      security:
        - agentClientAuth: []
      parameters:
        - $ref: "#/components/parameters/AgentClientIdHeader"
        - name: If-None-Match
          in: header
          schema:
            type: string
      responses:
        "200":
          description: The deterministic union of rules assigned through client groups.
        "304":
          description: The effective rule catalog has not changed.
        "401":
          $ref: "#/components/responses/Unauthorized"
  /agents/heartbeat:
    post:
      operationId: heartbeatAgentClient
      summary: Report client health and versions
      tags:
        - Agent monitoring
      security:
        - agentClientAuth: []
      parameters:
        - $ref: "#/components/parameters/AgentClientIdHeader"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                platform:
                  type: string
                architecture:
                  type: string
                cliVersion:
                  type: string
                engineVersion:
                  type: string
      responses:
        "200":
          description: Heartbeat accepted.
        "401":
          $ref: "#/components/responses/Unauthorized"
  /agents/records:
    get:
      operationId: listAgentAlerts
      summary: List agent alerts
      tags: [Agent monitoring]
      parameters:
        - { name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 25 } }
        - { name: offset, in: query, schema: { type: integer, minimum: 0, default: 0 } }
        - { name: client_id, in: query, schema: { type: string, format: uuid } }
        - { name: record_type, in: query, schema: { type: string, enum: [finding, enforcement] } }
      responses:
        "200":
          description: Endpoint findings and enforcement decisions.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentAlertListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      operationId: ingestAgentClientRecords
      summary: Ingest endpoint findings and enforcement decisions
      description: |
        Accepts up to 500 NDJSON records. Raw event records are rejected. The
        endpoint shipper passes the public client ID in the query string because
        its HTTP sink supports bearer authentication but not arbitrary headers.
      tags:
        - Agent monitoring
      security:
        - agentClientAuth: []
      parameters:
        - name: clientId
          in: query
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/x-ndjson:
            schema:
              type: string
      responses:
        "202":
          description: Records accepted and deduplicated.
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "413":
          description: Request body is too large.
  /agents/records/{record_id}:
    parameters:
      - { name: record_id, in: path, required: true, schema: { type: string, format: uuid } }
    get:
      operationId: getAgentAlert
      summary: Retrieve a privacy-filtered agent alert
      tags: [Agent monitoring]
      responses:
        "200":
          description: Unified finding and enforcement detail without raw event payloads.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AgentAlertDetailResponse"
        "404":
          $ref: "#/components/responses/NotFound"
  /agents/logs/stream:
    get:
      operationId: streamAgentLogs
      summary: Stream privacy-filtered agent logs
      description: |
        Server-sent events containing only the whitelisted monitor event fields.
        Resume with Last-Event-ID or the after and afterId query parameters.
      tags: [Agent monitoring]
      parameters:
        - { name: Last-Event-ID, in: header, schema: { type: string } }
        - { name: after, in: query, schema: { type: string, format: date-time } }
        - { name: afterId, in: query, schema: { type: string } }
      responses:
        "200":
          description: SSE events named ready, events, and error.
          content:
            text/event-stream:
              schema: { type: string }
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Superagent API key
      description: Organization API key beginning with `sk_live_`.
    agentClientAuth:
      type: http
      scheme: bearer
      bearerFormat: sa_client_*
      description: Revocable credential issued after pairing a registered client.
  parameters:
    AgentClientIdHeader:
      name: X-Superagent-Client-ID
      in: header
      required: true
      schema:
        type: string
        format: uuid
    FindingId:
      name: finding_id
      in: path
      required: true
      description: Finding identifier.
      schema:
        type: string
    ReportId:
      name: report_id
      in: path
      required: true
      description: Report identifier.
      schema:
        type: string
        format: uuid
    ContributorLogin:
      name: login
      in: path
      required: true
      description: GitHub username (case-insensitive).
      schema:
        type: string
        minLength: 1
        maxLength: 39
        pattern: "^(?:[A-Za-z0-9]|[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9]))$"
    ContributorTrustScanId:
      name: scan_id
      in: path
      required: true
      description: Contributor Trust scan identifier.
      schema:
        type: string
        format: uuid
  responses:
    BadRequest:
      description: The request body, field, query parameter, or state transition is invalid.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            error:
              code: invalid_request
              message: limit must be between 1 and 100
    Unauthorized:
      description: The bearer API key is missing or invalid.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            error:
              code: unauthorized
              message: "Provide a valid API key using Authorization: Bearer <api_key>"
    NotFound:
      description: The resource does not exist or belongs to another organization.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            error:
              code: not_found
              message: Finding not found
    Conflict:
      description: The requested work is already in progress.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            error:
              code: conflict
              message: Finding triage is already in progress
    InternalError:
      description: An unexpected server failure occurred.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            error:
              code: internal_error
              message: Internal server error
  schemas:
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - invalid_request
                - not_found
                - unauthorized
                - conflict
                - internal_error
            message:
              type: string
    ContributorTrustSignal:
      type: object
      required: [type, category, impact, source_status, detail]
      properties:
        type: { type: string }
        category: { type: string, enum: [identity, behavior, content] }
        impact: { type: string, enum: [positive, concern, context, unavailable] }
        source_status: { type: string, enum: [observed, unavailable, insufficient_sample] }
        detail: { type: string }
        score_effect: { type: number }
        severity: { type: string, enum: [critical, high, medium, low] }
        evidence:
          type: object
          additionalProperties:
            oneOf:
              - type: string
              - type: number
              - type: boolean
              - type: array
                items: { type: string }
    ContributorTrustThreat:
      type: object
      required: [type, detail, severity]
      properties:
        type: { type: string }
        detail: { type: string }
        severity: { type: string }
    ContributorTrustSubScores:
      type: object
      properties:
        identity: { type: number }
        behavior: { type: number }
        content: { type: number }
    ContributorTrustEvidenceCoverage:
      type: object
      required: [observed_sources, total_sources, ratio, core_available, unavailable_sources]
      properties:
        observed_sources: { type: integer, minimum: 0 }
        total_sources: { type: integer, minimum: 0 }
        ratio: { type: number, minimum: 0, maximum: 1 }
        core_available: { type: boolean }
        unavailable_sources:
          type: array
          items: { type: string }
        warnings:
          type: array
          items: { type: string }
    ContributorTrustAnalyzedPullRequest:
      type: object
      required: [repo, number]
      properties:
        repo: { type: string }
        number: { type: integer }
        title: { type: string }
        url: { type: string, format: uri }
        state: { type: string }
        merged: { type: boolean }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
        risk_reasons:
          type: array
          items: { type: string }
        verdict: { type: string, enum: [clean, suspicious, malicious, unknown] }
        evidence: { type: string }
        files_reviewed:
          type: array
          items: { type: string }
        suspicious_signals:
          type: array
          items: { type: string }
        clean_reason: { type: string }
        patch_mode: { type: string, enum: [full, preview, metadata] }
    ContributorTrustHistoryScan:
      type: object
      properties:
        status: { type: string, enum: [clean, review, inconclusive] }
        manifest_hash: { type: string }
        evidence_collected_at: { type: string, format: date-time }
        analyzed_pr_count: { type: integer, minimum: 0 }
        candidate_pr_count: { type: integer, minimum: 0 }
        omitted_pr_count: { type: integer, minimum: 0 }
        omitted_reason: { type: string }
        latency_ms: { type: number, minimum: 0 }
        evidence_collection_latency_ms: { type: number, minimum: 0 }
        sandbox_latency_ms: { type: number, minimum: 0 }
        shard_count: { type: integer, minimum: 0 }
        warnings:
          type: array
          items: { type: string }
    ContributorTrust:
      type: object
      required:
        - object
        - login
        - score
        - score_version
        - verdict
        - confidence
        - sub_scores
        - signals
        - threats
        - evidence_coverage
        - agent_summary
        - analyzed_prs
        - history_scan
        - scanned_at
      properties:
        object: { type: string, const: contributor_trust }
        login: { type: string }
        score: { type: [number, "null"] }
        score_version: { type: [string, "null"] }
        verdict: { type: [string, "null"] }
        confidence: { type: [string, "null"] }
        sub_scores:
          oneOf:
            - $ref: "#/components/schemas/ContributorTrustSubScores"
            - type: "null"
        signals:
          type: array
          items:
            $ref: "#/components/schemas/ContributorTrustSignal"
        threats:
          type: array
          items:
            $ref: "#/components/schemas/ContributorTrustThreat"
        evidence_coverage:
          oneOf:
            - $ref: "#/components/schemas/ContributorTrustEvidenceCoverage"
            - type: "null"
        agent_summary: { type: [string, "null"] }
        analyzed_prs:
          type: array
          items:
            $ref: "#/components/schemas/ContributorTrustAnalyzedPullRequest"
        history_scan:
          oneOf:
            - $ref: "#/components/schemas/ContributorTrustHistoryScan"
            - type: "null"
        scanned_at: { type: string, format: date-time }
    ContributorTrustResponse:
      type: object
      required: [data]
      properties:
        data:
          $ref: "#/components/schemas/ContributorTrust"
    ContributorTrustScanError:
      type: object
      required: [message]
      properties:
        message: { type: string }
    ContributorTrustScan:
      type: object
      required:
        - id
        - object
        - login
        - status
        - trust
        - error
        - started_at
        - completed_at
        - created_at
        - updated_at
      properties:
        id: { type: string, format: uuid }
        object: { type: string, const: contributor_trust_scan }
        login: { type: string }
        status: { type: string, enum: [queued, in_progress, completed, failed] }
        trust:
          oneOf:
            - $ref: "#/components/schemas/ContributorTrust"
            - type: "null"
        error:
          oneOf:
            - $ref: "#/components/schemas/ContributorTrustScanError"
            - type: "null"
        started_at: { type: [string, "null"], format: date-time }
        completed_at: { type: [string, "null"], format: date-time }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    ContributorTrustScanResponse:
      type: object
      required: [data]
      properties:
        data:
          $ref: "#/components/schemas/ContributorTrustScan"
    FindingKind:
      type: string
      enum:
        - repository_red_team
        - web_app_red_team
        - github_advisory
    TriageStatus:
      type: string
      enum:
        - new
        - triaging
        - in_review
        - resolved
    ManualTriageStatus:
      type: string
      enum:
        - new
        - in_review
        - resolved
    TriageResolution:
      type: string
      enum:
        - fixed
        - accepted_risk
        - false_positive
        - wont_fix
    Pagination:
      type: object
      required:
        - limit
        - offset
        - total
        - has_more
      properties:
        limit:
          type: integer
          minimum: 1
          maximum: 100
        offset:
          type: integer
          minimum: 0
        total:
          type: integer
          minimum: 0
        has_more:
          type: boolean
    AgentClient:
      type: object
      required: [id, object, organization_id, name, status, group_ids, created_at, updated_at]
      properties:
        id: { type: string, format: uuid }
        object: { type: string, const: agent_client }
        organization_id: { type: string, format: uuid }
        name: { type: string }
        status: { type: string, enum: [unpaired, active, revoked] }
        platform: { type: [string, "null"] }
        architecture: { type: [string, "null"] }
        cli_version: { type: [string, "null"] }
        last_seen_at: { type: [string, "null"], format: date-time }
        paired_at: { type: [string, "null"], format: date-time }
        revoked_at: { type: [string, "null"], format: date-time }
        group_ids:
          type: array
          items: { type: string, format: uuid }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    AgentClientResponse:
      type: object
      required: [data]
      properties:
        data:
          $ref: "#/components/schemas/AgentClient"
    AgentClientPairingResponse:
      type: object
      required: [data]
      properties:
        data:
          allOf:
            - $ref: "#/components/schemas/AgentClient"
            - type: object
              required: [pairing_token, pairing_token_expires_at]
              properties:
                pairing_token: { type: string }
                pairing_token_expires_at: { type: string, format: date-time }
    AgentClientListResponse:
      type: object
      required: [data, pagination]
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/AgentClient"
        pagination:
          $ref: "#/components/schemas/Pagination"
    AgentGroup:
      type: object
      required: [id, object, organization_id, name, client_ids, rule_ids, created_at, updated_at]
      properties:
        id: { type: string, format: uuid }
        object: { type: string, const: agent_group }
        organization_id: { type: string, format: uuid }
        name: { type: string }
        description: { type: [string, "null"] }
        client_ids:
          type: array
          items: { type: string, format: uuid }
        rule_ids:
          type: array
          items: { type: string, format: uuid }
        excluded_builtin_rule_ids:
          type: array
          description: Built-in Numbat rule identifiers disabled for this group.
          items: { type: string }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    AgentGroupResponse:
      type: object
      required: [data]
      properties:
        data:
          $ref: "#/components/schemas/AgentGroup"
    AgentGroupListResponse:
      type: object
      required: [data, pagination]
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/AgentGroup"
        pagination:
          $ref: "#/components/schemas/Pagination"
    AgentRule:
      type: object
      required: [id, object, organization_id, rule_id, version, title, severity, yaml_text, enforce, group_ids, created_at, updated_at]
      properties:
        id: { type: string, format: uuid }
        object: { type: string, const: agent_rule }
        organization_id: { type: string, format: uuid }
        rule_id: { type: string }
        version: { type: string }
        title: { type: string }
        severity: { type: string, enum: [info, low, medium, high, critical] }
        yaml_text: { type: string }
        enforce: { type: boolean }
        group_ids:
          type: array
          items: { type: string, format: uuid }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
    AgentRuleWriteRequest:
      type: object
      required: [yaml_text]
      properties:
        yaml_text: { type: string }
        group_ids:
          type: array
          items: { type: string, format: uuid }
    AgentRuleResponse:
      type: object
      required: [data]
      properties:
        data:
          $ref: "#/components/schemas/AgentRule"
    AgentRuleListResponse:
      type: object
      required: [data, pagination]
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/AgentRule"
        pagination:
          $ref: "#/components/schemas/Pagination"
    AgentBuiltinRule:
      type: object
      required: [id, object, version, title, category, severity, yaml_text, source, default_mode, effective_mode, override_state, effective_yaml_text]
      properties:
        id: { type: string }
        object: { type: string, const: agent_builtin_rule }
        version: { type: string }
        title: { type: string }
        description: { type: [string, "null"] }
        category: { type: string }
        severity: { type: string, enum: [info, low, medium, high, critical] }
        yaml_text: { type: string, description: Pinned shipped YAML. }
        source: { type: string, const: default }
        default_mode: { type: string, enum: [monitor, enforce, disabled] }
        effective_mode: { type: string, enum: [monitor, enforce, disabled] }
        override_state: { type: string, enum: [default, overridden] }
        effective_yaml_text: { type: string }
    AgentBuiltinRuleResponse:
      type: object
      required: [data]
      properties:
        data:
          $ref: "#/components/schemas/AgentBuiltinRule"
    AgentBuiltinRuleListResponse:
      type: object
      required: [data, pagination]
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/AgentBuiltinRule"
        pagination:
          $ref: "#/components/schemas/Pagination"
    AgentRuleValidationResponse:
      type: object
      required: [data]
      properties:
        data:
          type: object
          required: [valid, rule]
          properties:
            valid: { type: boolean, const: true }
            rule:
              type: object
              required: [id, version, title, severity, mode, yaml_text]
              properties:
                id: { type: string }
                version: { type: string }
                title: { type: string }
                severity: { type: string, enum: [info, low, medium, high, critical] }
                mode: { type: string, enum: [monitor, enforce, disabled] }
                yaml_text: { type: string }
    AgentRuleGenerationRequest:
      type: object
      required: [messages]
      properties:
        messages:
          type: array
          minItems: 1
          maxItems: 12
          items:
            type: object
            required: [role, content]
            properties:
              role: { type: string, enum: [user, assistant] }
              content: { type: string, minLength: 1, maxLength: 4000 }
        current_yaml: { type: string, maxLength: 131072 }
    AgentRuleGenerationResponse:
      type: object
      required: [data]
      properties:
        data:
          type: object
          required: [yaml, message, summary]
          properties:
            yaml: { type: string }
            message: { type: string }
            summary: { type: string }
    AgentAlert:
      type: object
      required: [id, object, client_id, record_type, payload, observed_at, created_at]
      properties:
        id: { type: string, format: uuid }
        object: { type: string, const: agent_alert }
        client_id: { type: string, format: uuid }
        record_type: { type: string, enum: [finding, enforcement] }
        severity: { type: [string, "null"], enum: [info, low, medium, high, critical, null] }
        rule_id: { type: [string, "null"] }
        payload: { type: object, additionalProperties: true }
        observed_at: { type: string, format: date-time }
        created_at: { type: string, format: date-time }
    AgentAlertListResponse:
      type: object
      required: [data, pagination]
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/AgentAlert"
        pagination:
          $ref: "#/components/schemas/Pagination"
    AgentAlertDetail:
      type: object
      required: [id, object, title, description, severity, outcome, client, groups, rule, sourceAgent, observedAt, evidence, timeline]
      properties:
        id: { type: string, format: uuid }
        object: { type: string, const: agent_alert_detail }
        title: { type: string }
        description: { type: string }
        severity: { type: [string, "null"], enum: [info, low, medium, high, critical, null] }
        outcome: { type: string, enum: [detected, observed, blocked] }
        client:
          type: object
          required: [id, name]
          properties:
            id: { type: string, format: uuid }
            name: { type: string }
        groups:
          type: array
          items:
            type: object
            required: [id, name]
            properties:
              id: { type: string, format: uuid }
              name: { type: string }
        rule:
          type: object
          required: [databaseId, id, version, mode, source, yaml, matchedExpression]
          properties:
            databaseId: { type: [string, "null"], format: uuid }
            id: { type: [string, "null"] }
            version: { type: [string, "null"] }
            mode: { type: [string, "null"], enum: [monitor, enforce, null] }
            source: { type: string, enum: [custom, built-in, unknown] }
            yaml: { type: [string, "null"] }
            matchedExpression: { type: [string, "null"] }
        sourceAgent: { type: [string, "null"] }
        observedAt: { type: string, format: date-time }
        evidence:
          type: object
          required: [actor, command]
          properties:
            actor: { type: [string, "null"] }
            command: { type: [string, "null"] }
        timeline:
          type: array
          items:
            type: object
            required: [id, externalId, kind, decision, observedAt]
            properties:
              id: { type: string, format: uuid }
              externalId: { type: string }
              kind: { type: string, enum: [finding, enforcement] }
              decision: { type: [string, "null"], enum: [deny, no_override, null] }
              observedAt: { type: string, format: date-time }
    AgentAlertDetailResponse:
      type: object
      required: [data]
      properties:
        data:
          $ref: "#/components/schemas/AgentAlertDetail"
    FindingReportSummary:
      type: object
      required:
        - repository
        - label
        - status
      properties:
        repository:
          type: string
          description: Repository URL, or tested target URL for a Web app finding.
        label:
          type: string
        status:
          type: string
    FindingSummary:
      type: object
      required:
        - id
        - object
        - kind
        - report_id
        - title
        - description
        - cvss_vector
        - severity
        - risk_level
        - weakness
        - cwe_ids
        - advisory_url
        - source
        - status
        - triage_status
        - triage_resolution
        - board_position
        - report
        - created_at
        - updated_at
      properties:
        id:
          type: string
        object:
          type: string
          const: finding
        kind:
          $ref: "#/components/schemas/FindingKind"
        report_id:
          type: string
        title:
          type: string
        description:
          type: string
        cvss_vector:
          type:
            - string
            - "null"
        severity:
          type:
            - string
            - "null"
        risk_level:
          type:
            - string
            - "null"
        weakness:
          type: string
        cwe_ids:
          type: array
          items:
            type: string
        advisory_url:
          type:
            - string
            - "null"
          format: uri
        source:
          type: string
        status:
          type:
            - string
            - "null"
        triage_status:
          $ref: "#/components/schemas/TriageStatus"
        triage_resolution:
          oneOf:
            - $ref: "#/components/schemas/TriageResolution"
            - type: "null"
        board_position:
          type:
            - number
            - "null"
        report:
          $ref: "#/components/schemas/FindingReportSummary"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    FindingListResponse:
      type: object
      required:
        - data
        - pagination
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/FindingSummary"
        pagination:
          $ref: "#/components/schemas/Pagination"
    AffectedProduct:
      type: object
      required:
        - ecosystem
        - packageName
        - affectedVersions
      properties:
        ecosystem:
          type: string
        packageName:
          type: string
        affectedVersions:
          type: string
    FindingTriage:
      type: object
      required:
        - status
        - resolution
        - summary
        - recommendation
        - report_markdown
        - verification_status
        - evidence
        - started_at
        - completed_at
      properties:
        status:
          $ref: "#/components/schemas/TriageStatus"
        resolution:
          oneOf:
            - $ref: "#/components/schemas/TriageResolution"
            - type: "null"
        summary:
          type:
            - string
            - "null"
        recommendation:
          type:
            - string
            - "null"
        report_markdown:
          type:
            - string
            - "null"
        verification_status:
          type:
            - string
            - "null"
          enum:
            - confirmed
            - not_reproducible
            - inconclusive
            - heuristic
            - null
        evidence:
          description: Structured evidence captured during triage.
        started_at:
          type:
            - string
            - "null"
          format: date-time
        completed_at:
          type:
            - string
            - "null"
          format: date-time
    ProposedPatch:
      type: object
      required:
        - summary
        - diff
        - files
      properties:
        summary:
          type: string
        diff:
          type: string
        files:
          type: array
          items:
            type: string
    CodeReference:
      type: object
      required:
        - file
        - lineStart
        - lineEnd
        - symbol
        - excerpt
        - reason
      properties:
        file:
          type: string
        lineStart:
          type:
            - integer
            - "null"
        lineEnd:
          type:
            - integer
            - "null"
        symbol:
          type:
            - string
            - "null"
        excerpt:
          type: string
        reason:
          type: string
    PullRequest:
      type: object
      required:
        - status
        - url
        - branch
        - started_at
        - completed_at
      properties:
        status:
          type: string
          enum:
            - idle
            - creating
            - created
            - failed
        url:
          type:
            - string
            - "null"
          format: uri
        branch:
          type:
            - string
            - "null"
        started_at:
          type:
            - string
            - "null"
          format: date-time
        completed_at:
          type:
            - string
            - "null"
          format: date-time
    FindingRemediation:
      type: object
      required:
        - proposed_patch
        - code_references
        - pull_request
      properties:
        proposed_patch:
          oneOf:
            - $ref: "#/components/schemas/ProposedPatch"
            - type: "null"
        code_references:
          type: array
          items:
            $ref: "#/components/schemas/CodeReference"
        pull_request:
          $ref: "#/components/schemas/PullRequest"
    Finding:
      type: object
      required:
        - id
        - object
        - kind
        - report_id
        - title
        - description
        - cvss_vector
        - severity
        - risk_level
        - weakness
        - cwe_ids
        - affected_products
        - advisory_url
        - source
        - status
        - triage
        - remediation
        - board_position
        - report
        - created_at
        - updated_at
      properties:
        id:
          type: string
        object:
          type: string
          const: finding
        kind:
          $ref: "#/components/schemas/FindingKind"
        report_id:
          type: string
        title:
          type: string
        description:
          type: string
        cvss_vector:
          type:
            - string
            - "null"
        severity:
          type:
            - string
            - "null"
        risk_level:
          type:
            - string
            - "null"
        weakness:
          type: string
        cwe_ids:
          type: array
          items:
            type: string
        affected_products:
          type: array
          items:
            $ref: "#/components/schemas/AffectedProduct"
        advisory_url:
          type:
            - string
            - "null"
          format: uri
        source:
          type: string
        status:
          type:
            - string
            - "null"
        triage:
          $ref: "#/components/schemas/FindingTriage"
        remediation:
          $ref: "#/components/schemas/FindingRemediation"
        board_position:
          type:
            - number
            - "null"
        report:
          $ref: "#/components/schemas/FindingReportSummary"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    FindingResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/Finding"
    UpdateFindingRequest:
      type: object
      required:
        - triage_status
      properties:
        triage_status:
          $ref: "#/components/schemas/ManualTriageStatus"
        triage_resolution:
          oneOf:
            - $ref: "#/components/schemas/TriageResolution"
            - type: "null"
          description: Required when `triage_status` is `resolved`.
        board_position:
          description: Board position; unsupported for Web app findings.
          oneOf:
            - type: number
            - type: string
              pattern: "^[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)$"
            - type: "null"
    TriageResult:
      type: object
      required:
        - id
        - object
        - kind
        - triage_status
      properties:
        id:
          type: string
        object:
          type: string
          const: finding
        kind:
          $ref: "#/components/schemas/FindingKind"
        triage_status:
          type: string
          const: triaging
    TriageResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/TriageResult"
    CreateRepositoryReportRequest:
      type: object
      required:
        - repository
      properties:
        repository:
          type: string
          minLength: 1
          maxLength: 512
          description: Connected repository as `owner/name` or a full GitHub URL.
        custom_goal_prompt:
          type:
            - string
            - "null"
          maxLength: 8000
    CreateWebAppReportRequest:
      type: object
      required:
        - target_url
      properties:
        target_url:
          type: string
          minLength: 1
          maxLength: 2048
          format: uri
          pattern: "^https?://"
          description: |
            Public HTTP or HTTPS URL. Localhost, `.local`, and private or
            otherwise non-public IP addresses are rejected.
        login_email:
          type:
            - string
            - "null"
          maxLength: 320
        login_password:
          type:
            - string
            - "null"
          maxLength: 4096
          format: password
          writeOnly: true
        browser_headers:
          oneOf:
            - type: object
              maxProperties: 20
              propertyNames:
                type: string
                maxLength: 128
                pattern: "^[!#$%&'*+.^_`|~0-9A-Za-z-]+$"
              additionalProperties:
                type: string
                minLength: 1
                maxLength: 4096
                pattern: "^(?!\\s*$)[^\\r\\n]+$"
            - type: "null"
          description: |
            Header names must be unique case-insensitively and use valid HTTP
            token characters. Names and values cannot be blank after trimming;
            values cannot contain line breaks.
          writeOnly: true
        request_throttle_rpm:
          oneOf:
            - type: integer
              minimum: 1
              maximum: 600
            - type: string
              pattern: "^\\s*0*(?:[1-9]|[1-9]\\d|[1-5]\\d{2}|600)\\s*$"
            - type: string
              pattern: "^\\s*$"
              description: A blank string is treated as no request limit.
            - type: "null"
        custom_goal_prompt:
          type:
            - string
            - "null"
          maxLength: 8000
    CreateAgentReportRequest:
      type: object
      required:
        - target_type
        - target_url
      properties:
        target_type:
          type: string
          enum:
            - web_app
            - api
          description: Interaction modality used by the Agent red-team runner.
        target_url:
          type: string
          minLength: 1
          maxLength: 2048
          format: uri
          description: Public Web app or API endpoint URL.
        agent_type:
          type: string
          default: chatbot
          enum:
            - chatbot
            - voice_agent
            - coding_agent
            - ai_workflow
            - data_pipeline
            - other
        description:
          type:
            - string
            - "null"
          maxLength: 1000
          description: Optional report label; defaults to the target host.
        login_email:
          type:
            - string
            - "null"
          maxLength: 320
          description: Optional login email for Web app targets.
        login_password:
          type:
            - string
            - "null"
          maxLength: 4096
          format: password
          writeOnly: true
          description: Optional login password for Web app targets.
        headers:
          oneOf:
            - type: object
              maxProperties: 20
              propertyNames:
                type: string
                maxLength: 128
                pattern: "^[!#$%&'*+.^_`|~0-9A-Za-z-]+$"
              additionalProperties:
                type: string
                minLength: 1
                maxLength: 4096
                pattern: "^(?!\\s*$)[^\\r\\n]+$"
            - type: "null"
          description: Optional encrypted HTTP headers sent to the target.
          writeOnly: true
        request_throttle_rpm:
          oneOf:
            - type: integer
              minimum: 1
              maximum: 600
            - type: string
              pattern: "^\\s*0*(?:[1-9]|[1-9]\\d|[1-5]\\d{2}|600)\\s*$"
            - type: string
              pattern: "^\\s*$"
            - type: "null"
        custom_goal_prompt:
          type:
            - string
            - "null"
          maxLength: 8000
          description: |
            Optional attack focus. For API targets, include example request
            payloads and expected response shapes.
    ReportCommon:
      type: object
      required:
        - id
        - object
        - status
        - sandbox_status
        - agent_status
        - created_at
        - updated_at
      properties:
        id:
          type: string
        object:
          type: string
          const: report
        status:
          type: string
        sandbox_status:
          type: string
        agent_status:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    RepositoryReport:
      allOf:
        - $ref: "#/components/schemas/ReportCommon"
        - type: object
          required:
            - type
            - repository
            - custom_goal_prompt
          properties:
            type:
              type: string
              const: repository
            repository:
              type: string
              format: uri
            custom_goal_prompt:
              type:
                - string
                - "null"
    WebAppReport:
      allOf:
        - $ref: "#/components/schemas/ReportCommon"
        - type: object
          required:
            - type
            - target_url
            - allowed_host
            - request_throttle_rpm
            - custom_goal_prompt
          properties:
            type:
              type: string
              const: web_app
            target_url:
              type: string
              format: uri
            allowed_host:
              type: string
            request_throttle_rpm:
              type:
                - integer
                - "null"
            custom_goal_prompt:
              type:
                - string
                - "null"
    AgentReport:
      allOf:
        - $ref: "#/components/schemas/ReportCommon"
        - type: object
          required:
            - type
            - description
            - agent_type
            - target_type
            - target_url
            - allowed_host
            - request_throttle_rpm
            - custom_goal_prompt
          properties:
            type:
              type: string
              const: agent
            description:
              type: string
            agent_type:
              type: string
            target_type:
              oneOf:
                - type: string
                  enum:
                    - web_app
                    - api
                - type: "null"
            target_url:
              type:
                - string
                - "null"
              format: uri
            allowed_host:
              type:
                - string
                - "null"
            request_throttle_rpm:
              type:
                - integer
                - "null"
            custom_goal_prompt:
              type:
                - string
                - "null"
    ReportType:
      type: string
      enum:
        - repository
        - web_app
        - agent
    ReportStatus:
      type: string
      enum:
        - in_progress
        - in_review
        - done
        - failed
    Report:
      oneOf:
        - $ref: "#/components/schemas/RepositoryReport"
        - $ref: "#/components/schemas/WebAppReport"
        - $ref: "#/components/schemas/AgentReport"
      discriminator:
        propertyName: type
        mapping:
          repository: "#/components/schemas/RepositoryReport"
          web_app: "#/components/schemas/WebAppReport"
          agent: "#/components/schemas/AgentReport"
    ReportListResponse:
      type: object
      required:
        - data
        - pagination
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Report"
        pagination:
          $ref: "#/components/schemas/Pagination"
    ReportResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/Report"
    RepositoryReportResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/RepositoryReport"
    WebAppReportResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/WebAppReport"
    AgentReportResponse:
      type: object
      required:
        - data
      properties:
        data:
          $ref: "#/components/schemas/AgentReport"
