openapi: 3.1.0 info: title: StellaOps Findings Ledger API version: 1.0.0 description: | OpenAPI specification for the Findings Ledger service. Unblocks LEDGER-OAS-61-001-DEV through LEDGER-OAS-63-001-DEV. contact: name: StellaOps API Team email: api@stella-ops.org license: name: AGPL-3.0-or-later identifier: AGPL-3.0-or-later servers: - url: https://api.stella-ops.org/v1 description: Production - url: https://api.staging.stella-ops.org/v1 description: Staging tags: - name: findings description: Finding management operations - name: projections description: Finding projections and views - name: evidence description: Evidence lookups and links - name: snapshots description: Time-travel and snapshot operations - name: attestation description: Attestation and verification - name: export description: Export and reporting paths: /findings: get: operationId: listFindings summary: List findings with pagination and filtering tags: [findings] parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/ProjectId' - $ref: '#/components/parameters/PageSize' - $ref: '#/components/parameters/PageToken' - $ref: '#/components/parameters/SortBy' - $ref: '#/components/parameters/SortOrder' - name: status in: query schema: type: array items: $ref: '#/components/schemas/FindingStatus' - name: severity in: query schema: type: array items: $ref: '#/components/schemas/Severity' - name: component_purl in: query schema: type: string description: Filter by component PURL pattern - name: vulnerability_id in: query schema: type: string description: Filter by CVE or vulnerability ID - name: created_after in: query schema: type: string format: date-time - name: created_before in: query schema: type: string format: date-time responses: '200': description: Paginated list of findings content: application/json: schema: $ref: '#/components/schemas/FindingsListResponse' headers: ETag: schema: type: string description: Entity tag for caching '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' post: operationId: createFinding summary: Create a new finding tags: [findings] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateFindingRequest' responses: '201': description: Finding created content: application/json: schema: $ref: '#/components/schemas/Finding' headers: Location: schema: type: string format: uri '400': $ref: '#/components/responses/BadRequest' '409': $ref: '#/components/responses/Conflict' /findings/{findingId}: get: operationId: getFinding summary: Get finding by ID tags: [findings] parameters: - $ref: '#/components/parameters/FindingId' - name: include in: query schema: type: array items: type: string enum: [evidence, attestations, history, projections] description: Related data to include responses: '200': description: Finding details content: application/json: schema: $ref: '#/components/schemas/Finding' headers: ETag: schema: type: string '304': description: Not modified '404': $ref: '#/components/responses/NotFound' patch: operationId: updateFinding summary: Update finding status or metadata tags: [findings] parameters: - $ref: '#/components/parameters/FindingId' - name: If-Match in: header required: true schema: type: string description: ETag for optimistic concurrency requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateFindingRequest' responses: '200': description: Finding updated content: application/json: schema: $ref: '#/components/schemas/Finding' '412': $ref: '#/components/responses/PreconditionFailed' /findings/{findingId}/evidence: get: operationId: getFindingEvidence summary: Get evidence linked to a finding tags: [findings, evidence] parameters: - $ref: '#/components/parameters/FindingId' - name: artifact_type in: query schema: type: array items: type: string enum: [sbom, vex, scan_result, attestation, callgraph, runtime_facts] responses: '200': description: Evidence list content: application/json: schema: $ref: '#/components/schemas/EvidenceListResponse' /findings/{findingId}/attestations: get: operationId: getFindingAttestations summary: Get attestations for a finding tags: [findings, attestation] parameters: - $ref: '#/components/parameters/FindingId' responses: '200': description: Attestation list content: application/json: schema: $ref: '#/components/schemas/AttestationListResponse' /findings/{findingId}/history: get: operationId: getFindingHistory summary: Get finding status history tags: [findings] parameters: - $ref: '#/components/parameters/FindingId' responses: '200': description: History entries content: application/json: schema: $ref: '#/components/schemas/HistoryListResponse' /projections: get: operationId: listProjections summary: List available projections tags: [projections] parameters: - $ref: '#/components/parameters/TenantId' responses: '200': description: Projection list content: application/json: schema: $ref: '#/components/schemas/ProjectionListResponse' /projections/{projectionId}: get: operationId: getProjection summary: Get projection data tags: [projections] parameters: - name: projectionId in: path required: true schema: type: string - name: filter in: query schema: type: string description: JSON filter expression - $ref: '#/components/parameters/PageSize' - $ref: '#/components/parameters/PageToken' responses: '200': description: Projection data content: application/json: schema: $ref: '#/components/schemas/ProjectionDataResponse' /snapshots: get: operationId: listSnapshots summary: List available snapshots tags: [snapshots] parameters: - $ref: '#/components/parameters/TenantId' - $ref: '#/components/parameters/ProjectId' responses: '200': description: Snapshot list content: application/json: schema: $ref: '#/components/schemas/SnapshotListResponse' post: operationId: createSnapshot summary: Create a point-in-time snapshot tags: [snapshots] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateSnapshotRequest' responses: '202': description: Snapshot creation accepted content: application/json: schema: $ref: '#/components/schemas/SnapshotJob' /snapshots/{snapshotId}: get: operationId: getSnapshot summary: Get snapshot details tags: [snapshots] parameters: - name: snapshotId in: path required: true schema: type: string format: uuid responses: '200': description: Snapshot details content: application/json: schema: $ref: '#/components/schemas/Snapshot' /snapshots/{snapshotId}/findings: get: operationId: getSnapshotFindings summary: Get findings from a snapshot (time-travel query) tags: [snapshots] parameters: - name: snapshotId in: path required: true schema: type: string format: uuid - $ref: '#/components/parameters/PageSize' - $ref: '#/components/parameters/PageToken' responses: '200': description: Findings at snapshot point content: application/json: schema: $ref: '#/components/schemas/FindingsListResponse' /evidence: get: operationId: listEvidence summary: List evidence artifacts tags: [evidence] parameters: - $ref: '#/components/parameters/TenantId' - name: artifact_type in: query schema: type: array items: type: string - name: digest in: query schema: type: string pattern: '^sha256:[a-f0-9]{64}$' responses: '200': description: Evidence list content: application/json: schema: $ref: '#/components/schemas/EvidenceListResponse' /evidence/{evidenceId}: get: operationId: getEvidence summary: Get evidence artifact tags: [evidence] parameters: - name: evidenceId in: path required: true schema: type: string format: uuid responses: '200': description: Evidence details content: application/json: schema: $ref: '#/components/schemas/EvidenceArtifact' /export: post: operationId: createExport summary: Create export job for findings tags: [export] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateExportRequest' responses: '202': description: Export job created content: application/json: schema: $ref: '#/components/schemas/ExportJob' /export/{exportId}: get: operationId: getExport summary: Get export job status and download tags: [export] parameters: - name: exportId in: path required: true schema: type: string format: uuid responses: '200': description: Export job details content: application/json: schema: $ref: '#/components/schemas/ExportJob' /.well-known/openapi: get: operationId: getOpenApiSpec summary: Get OpenAPI specification description: Returns the OpenAPI specification for this API responses: '200': description: OpenAPI specification content: application/json: schema: type: object application/yaml: schema: type: object components: parameters: TenantId: name: X-Tenant-ID in: header required: true schema: type: string format: uuid description: Tenant identifier ProjectId: name: X-Project-ID in: header schema: type: string format: uuid description: Project identifier FindingId: name: findingId in: path required: true schema: type: string format: uuid description: Finding identifier PageSize: name: page_size in: query schema: type: integer minimum: 1 maximum: 1000 default: 100 PageToken: name: page_token in: query schema: type: string description: Continuation token for pagination SortBy: name: sort_by in: query schema: type: string enum: [created_at, updated_at, severity, status] default: created_at SortOrder: name: sort_order in: query schema: type: string enum: [asc, desc] default: desc schemas: Finding: type: object required: - id - tenant_id - vulnerability_id - component - status - severity - created_at properties: id: type: string format: uuid tenant_id: type: string format: uuid project_id: type: string format: uuid vulnerability_id: type: string description: CVE ID or vulnerability identifier component: $ref: '#/components/schemas/Component' status: $ref: '#/components/schemas/FindingStatus' severity: $ref: '#/components/schemas/Severity' cvss_score: type: number minimum: 0 maximum: 10 epss_score: type: number minimum: 0 maximum: 1 kev_listed: type: boolean reachability: $ref: '#/components/schemas/ReachabilityInfo' vex_status: type: string enum: [not_affected, affected, fixed, under_investigation] fix_available: type: boolean fix_version: type: string source: type: string description: Source of the finding (scanner name) labels: type: object additionalProperties: type: string created_at: type: string format: date-time updated_at: type: string format: date-time first_seen_at: type: string format: date-time last_seen_at: type: string format: date-time evidence_refs: type: array items: $ref: '#/components/schemas/EvidenceRef' attestation_refs: type: array items: $ref: '#/components/schemas/AttestationRef' FindingStatus: type: string enum: - open - triaged - in_progress - resolved - ignored - false_positive Severity: type: string enum: - critical - high - medium - low - info Component: type: object required: - purl properties: purl: type: string description: Package URL name: type: string version: type: string ecosystem: type: string digest: type: string ReachabilityInfo: type: object properties: state: type: string enum: [reachable, unreachable, potentially_reachable, unknown] confidence: type: number minimum: 0 maximum: 1 entry_points: type: array items: type: string EvidenceRef: type: object required: - id - digest properties: id: type: string format: uuid artifact_type: type: string digest: type: string uri: type: string format: uri AttestationRef: type: object required: - id properties: id: type: string format: uuid type: type: string digest: type: string CreateFindingRequest: type: object required: - vulnerability_id - component - severity properties: vulnerability_id: type: string component: $ref: '#/components/schemas/Component' severity: $ref: '#/components/schemas/Severity' source: type: string labels: type: object additionalProperties: type: string evidence_refs: type: array items: $ref: '#/components/schemas/EvidenceRef' UpdateFindingRequest: type: object properties: status: $ref: '#/components/schemas/FindingStatus' severity: $ref: '#/components/schemas/Severity' labels: type: object additionalProperties: type: string notes: type: string FindingsListResponse: type: object required: - findings - total_count properties: findings: type: array items: $ref: '#/components/schemas/Finding' total_count: type: integer next_page_token: type: string EvidenceArtifact: type: object required: - id - artifact_type - digest properties: id: type: string format: uuid artifact_type: type: string digest: type: string content_type: type: string size_bytes: type: integer storage_uri: type: string format: uri created_at: type: string format: date-time provenance: type: object EvidenceListResponse: type: object required: - evidence properties: evidence: type: array items: $ref: '#/components/schemas/EvidenceArtifact' total_count: type: integer next_page_token: type: string AttestationListResponse: type: object required: - attestations properties: attestations: type: array items: type: object total_count: type: integer HistoryListResponse: type: object required: - entries properties: entries: type: array items: type: object properties: timestamp: type: string format: date-time actor: type: string action: type: string changes: type: object ProjectionListResponse: type: object required: - projections properties: projections: type: array items: type: object properties: id: type: string name: type: string description: type: string ProjectionDataResponse: type: object required: - data properties: data: type: array items: type: object total_count: type: integer next_page_token: type: string Snapshot: type: object required: - id - created_at - status properties: id: type: string format: uuid name: type: string description: type: string created_at: type: string format: date-time point_in_time: type: string format: date-time status: type: string enum: [pending, ready, expired, failed] finding_count: type: integer digest: type: string SnapshotListResponse: type: object required: - snapshots properties: snapshots: type: array items: $ref: '#/components/schemas/Snapshot' total_count: type: integer CreateSnapshotRequest: type: object properties: name: type: string description: type: string point_in_time: type: string format: date-time description: Optional specific point in time (defaults to now) SnapshotJob: type: object required: - id - status properties: id: type: string format: uuid status: type: string enum: [queued, processing, completed, failed] snapshot_id: type: string format: uuid progress: type: integer minimum: 0 maximum: 100 CreateExportRequest: type: object properties: format: type: string enum: [json, csv, sarif, cyclonedx, spdx] default: json filters: type: object properties: status: type: array items: $ref: '#/components/schemas/FindingStatus' severity: type: array items: $ref: '#/components/schemas/Severity' created_after: type: string format: date-time created_before: type: string format: date-time ExportJob: type: object required: - id - status properties: id: type: string format: uuid status: type: string enum: [queued, processing, completed, failed] format: type: string download_url: type: string format: uri expires_at: type: string format: date-time finding_count: type: integer Error: type: object required: - code - message properties: code: type: string message: type: string details: type: object trace_id: type: string responses: BadRequest: description: Bad request content: application/json: schema: $ref: '#/components/schemas/Error' Unauthorized: description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error' Forbidden: description: Forbidden content: application/json: schema: $ref: '#/components/schemas/Error' NotFound: description: Not found content: application/json: schema: $ref: '#/components/schemas/Error' Conflict: description: Conflict content: application/json: schema: $ref: '#/components/schemas/Error' PreconditionFailed: description: Precondition failed (ETag mismatch) content: application/json: schema: $ref: '#/components/schemas/Error' securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT oauth2: type: oauth2 flows: clientCredentials: tokenUrl: https://auth.stella-ops.org/oauth/token scopes: findings:read: Read findings findings:write: Write findings evidence:read: Read evidence snapshots:read: Read snapshots snapshots:write: Create snapshots export:write: Create exports security: - bearerAuth: [] - oauth2: [findings:read]