openapi: 3.1.0 info: title: Findings Workflow Semantic Contract version: 0.1.0 description: | Semantic contract for tenant-context findings workflow operations and audit review. These endpoints describe the canonical server-side behavior required by the feature, even if concrete implementation remains behind Filament and Livewire action handlers. Human-driven and system-driven lifecycle mutations share the same server-side transition gateway and audit contract. servers: - url: http://localhost paths: /admin/t/{tenant}/findings/{finding}: get: summary: View a tenant-scoped finding operationId: viewFinding parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/FindingId' responses: '200': description: Finding returned within the entitled tenant scope content: application/json: schema: $ref: '#/components/schemas/Finding' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' /admin/t/{tenant}/findings/{finding}/workflow/triage: post: summary: Triage a finding operationId: triageFinding parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/FindingId' responses: '200': $ref: '#/components/responses/FindingMutationSucceeded' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/InvalidTransition' /admin/t/{tenant}/findings/{finding}/workflow/start-progress: post: summary: Move a finding to in progress operationId: startFindingProgress parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/FindingId' responses: '200': $ref: '#/components/responses/FindingMutationSucceeded' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/InvalidTransition' /admin/t/{tenant}/findings/{finding}/workflow/assign: post: summary: Assign finding ownership or assignee operationId: assignFinding parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/FindingId' requestBody: required: true content: application/json: schema: type: object properties: assignee_user_id: type: - integer - 'null' owner_user_id: type: - integer - 'null' responses: '200': $ref: '#/components/responses/FindingMutationSucceeded' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/InvalidTransition' '422': $ref: '#/components/responses/ValidationError' /admin/t/{tenant}/findings/{finding}/workflow/resolve: post: summary: Resolve a finding operationId: resolveFinding parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/FindingId' requestBody: required: true content: application/json: schema: type: object required: [reason] properties: reason: type: string responses: '200': $ref: '#/components/responses/FindingMutationSucceeded' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/InvalidTransition' '422': $ref: '#/components/responses/ValidationError' /admin/t/{tenant}/findings/{finding}/workflow/close: post: summary: Close a finding operationId: closeFinding parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/FindingId' requestBody: required: true content: application/json: schema: type: object required: [reason] properties: reason: type: string responses: '200': $ref: '#/components/responses/FindingMutationSucceeded' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/InvalidTransition' '422': $ref: '#/components/responses/ValidationError' /admin/t/{tenant}/findings/{finding}/workflow/risk-accept: post: summary: Mark a finding as risk accepted operationId: riskAcceptFinding parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/FindingId' requestBody: required: true content: application/json: schema: type: object required: [reason] properties: reason: type: string responses: '200': $ref: '#/components/responses/FindingMutationSucceeded' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/InvalidTransition' '422': $ref: '#/components/responses/ValidationError' /admin/t/{tenant}/findings/{finding}/workflow/reopen: post: summary: Reopen a terminal finding operationId: reopenFinding parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/FindingId' responses: '200': $ref: '#/components/responses/FindingMutationSucceeded' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/InvalidTransition' /admin/t/{tenant}/findings/workflow/bulk: post: summary: Execute a bulk workflow mutation over multiple findings operationId: bulkMutateFindings parameters: - $ref: '#/components/parameters/TenantId' requestBody: required: true content: application/json: schema: type: object required: [operation, finding_ids] properties: operation: type: string enum: [triage, assign, resolve, close, risk_accept, reopen] finding_ids: type: array items: type: integer payload: type: object additionalProperties: true responses: '200': description: Bulk mutation completed with per-record audit history content: application/json: schema: type: object required: [processed, succeeded, failed] properties: processed: type: integer succeeded: type: integer failed: type: integer '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': $ref: '#/components/responses/InvalidTransition' /admin/audit-log: get: summary: Review finding-related audit history on the canonical audit surface operationId: listFindingAuditEvents parameters: - name: resource_type in: query schema: type: string enum: [finding] - name: tenant_id in: query schema: type: integer responses: '200': description: Audit events visible within current workspace and tenant entitlement content: application/json: schema: type: array items: $ref: '#/components/schemas/FindingAuditEvent' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' components: parameters: TenantId: name: tenant in: path required: true schema: type: integer FindingId: name: finding in: path required: true schema: type: integer responses: FindingMutationSucceeded: description: Workflow mutation completed and durable audit history was written content: application/json: schema: type: object required: [finding, audit_event] properties: finding: $ref: '#/components/schemas/Finding' audit_event: $ref: '#/components/schemas/FindingAuditEvent' Forbidden: description: Viewer is in scope but lacks required capability NotFound: description: Viewer is not entitled to the requested workspace or tenant scope, or the record is outside that scope, including workspace-mismatched findings in canonical tenant context InvalidTransition: description: Requested lifecycle mutation is not valid from the current state ValidationError: description: Request payload is syntactically valid but missing required business fields schemas: Finding: type: object required: [id, tenant_id, workspace_id, status, severity] properties: id: type: integer tenant_id: type: integer workspace_id: type: integer status: type: string enum: [new, acknowledged, triaged, in_progress, reopened, resolved, closed, risk_accepted] severity: type: string enum: [low, medium, high, critical] due_at: type: - string - 'null' format: date-time resolved_reason: type: - string - 'null' closed_reason: type: - string - 'null' FindingAuditEvent: type: object required: [action, resource_type, resource_id, tenant_id, workspace_id, metadata] properties: action: type: string example: finding.resolved resource_type: type: string enum: [finding] resource_id: type: string tenant_id: type: - integer - 'null' workspace_id: type: integer metadata: $ref: './finding-audit-event.schema.yaml#/components/schemas/FindingAuditMetadata' actor_type: type: string enum: [human, system, scheduled, integration, platform] description: Distinguishes system-origin workflow mutations from human-initiated actions on the canonical audit surface.