openapi: 3.1.0 info: title: Finding Risk Acceptance Internal Contract version: 0.1.0 description: | Internal admin-plane contract for the Finding Risk Acceptance Lifecycle. These endpoints represent the server-side action contract backing Filament and Livewire surfaces. servers: - url: /api/internal paths: /tenants/{tenantId}/findings/{findingId}/exception-requests: post: summary: Request exception operationId: requestFindingException tags: [Finding Exceptions] parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/FindingId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RequestExceptionInput' responses: '201': description: Exception request created content: application/json: schema: $ref: '#/components/schemas/FindingExceptionResource' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' /tenants/{tenantId}/finding-exceptions: get: summary: List tenant exceptions operationId: listTenantFindingExceptions tags: [Finding Exceptions] parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/StateFilter' - $ref: '#/components/parameters/DueFilter' responses: '200': description: Tenant exception register content: application/json: schema: type: object properties: data: type: array items: $ref: '#/components/schemas/FindingExceptionResource' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' /tenants/{tenantId}/finding-exceptions/{exceptionId}: get: summary: View exception detail operationId: showFindingException tags: [Finding Exceptions] parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/ExceptionId' responses: '200': description: Exception detail content: application/json: schema: $ref: '#/components/schemas/FindingExceptionDetail' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' /tenants/{tenantId}/finding-exceptions/{exceptionId}/approve: post: summary: Approve exception operationId: approveFindingException tags: [Finding Exceptions] parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/ExceptionId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ApproveExceptionInput' responses: '200': description: Exception approved content: application/json: schema: $ref: '#/components/schemas/FindingExceptionResource' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/ValidationError' /tenants/{tenantId}/finding-exceptions/{exceptionId}/reject: post: summary: Reject exception operationId: rejectFindingException tags: [Finding Exceptions] parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/ExceptionId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RejectExceptionInput' responses: '200': description: Exception rejected content: application/json: schema: $ref: '#/components/schemas/FindingExceptionResource' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/ValidationError' /tenants/{tenantId}/finding-exceptions/{exceptionId}/renew: post: summary: Renew exception operationId: renewFindingException tags: [Finding Exceptions] parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/ExceptionId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RenewExceptionInput' responses: '200': description: Renewal request accepted content: application/json: schema: $ref: '#/components/schemas/FindingExceptionResource' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/ValidationError' /tenants/{tenantId}/finding-exceptions/{exceptionId}/revoke: post: summary: Revoke exception operationId: revokeFindingException tags: [Finding Exceptions] parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/ExceptionId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RevokeExceptionInput' responses: '200': description: Exception revoked content: application/json: schema: $ref: '#/components/schemas/FindingExceptionResource' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/ValidationError' /workspaces/{workspaceId}/finding-exceptions/queue: get: summary: Canonical exception approval queue operationId: listCanonicalFindingExceptionQueue tags: [Finding Exceptions] parameters: - $ref: '#/components/parameters/WorkspaceId' - $ref: '#/components/parameters/StateFilter' - $ref: '#/components/parameters/DueFilter' - name: tenantId in: query schema: type: integer responses: '200': description: Canonical queue filtered to entitled tenants content: application/json: schema: type: object properties: data: type: array items: $ref: '#/components/schemas/FindingExceptionResource' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' components: parameters: TenantId: name: tenantId in: path required: true schema: type: integer FindingId: name: findingId in: path required: true schema: type: integer ExceptionId: name: exceptionId in: path required: true schema: type: integer WorkspaceId: name: workspaceId in: path required: true schema: type: integer StateFilter: name: state in: query schema: type: string enum: [pending, active, expiring, expired, rejected, revoked, superseded] DueFilter: name: due in: query schema: type: string enum: [all, expiring, expired] responses: Forbidden: description: Member lacks required capability NotFound: description: Workspace or tenant scope is not entitled ValidationError: description: Input or transition validation failed schemas: FindingExceptionResource: type: object required: - id - tenant_id - finding_id - status - validity_state properties: id: type: integer tenant_id: type: integer finding_id: type: integer status: type: string enum: [pending, active, expiring, expired, rejected, revoked, superseded] validity_state: type: string enum: [valid, expiring, expired, revoked, rejected, missing_support] owner_user_id: type: integer nullable: true requested_by_user_id: type: integer approved_by_user_id: type: integer nullable: true requested_at: type: string format: date-time approved_at: type: string format: date-time nullable: true expires_at: type: string format: date-time nullable: true review_due_at: type: string format: date-time nullable: true FindingExceptionDetail: allOf: - $ref: '#/components/schemas/FindingExceptionResource' - type: object properties: request_reason: type: string decisions: type: array items: $ref: '#/components/schemas/FindingExceptionDecision' evidence_references: type: array items: $ref: '#/components/schemas/FindingExceptionEvidenceReference' FindingExceptionDecision: type: object required: - decision_type - actor_user_id - decided_at properties: decision_type: type: string enum: [requested, approved, rejected, renewal_requested, renewed, revoked] actor_user_id: type: integer reason: type: string nullable: true effective_from: type: string format: date-time nullable: true expires_at: type: string format: date-time nullable: true decided_at: type: string format: date-time FindingExceptionEvidenceReference: type: object required: - source_type - label properties: source_type: type: string source_id: type: string nullable: true source_fingerprint: type: string nullable: true label: type: string measured_at: type: string format: date-time nullable: true summary_payload: type: object additionalProperties: true nullable: true RequestExceptionInput: type: object required: - owner_user_id - request_reason - review_due_at properties: owner_user_id: type: integer request_reason: type: string maxLength: 2000 review_due_at: type: string format: date-time expires_at: type: string format: date-time nullable: true evidence_references: type: array items: $ref: '#/components/schemas/FindingExceptionEvidenceReference' ApproveExceptionInput: type: object required: - effective_from - expires_at properties: effective_from: type: string format: date-time expires_at: type: string format: date-time approval_reason: type: string nullable: true maxLength: 2000 RejectExceptionInput: type: object required: - rejection_reason properties: rejection_reason: type: string maxLength: 2000 RenewExceptionInput: type: object required: - request_reason - review_due_at properties: request_reason: type: string maxLength: 2000 review_due_at: type: string format: date-time expires_at: type: string format: date-time nullable: true evidence_references: type: array items: $ref: '#/components/schemas/FindingExceptionEvidenceReference' RevokeExceptionInput: type: object required: - revocation_reason properties: revocation_reason: type: string maxLength: 2000