# Excititor Advisory-AI evidence APIs (projection + chunks) > Covers the read-only evidence surfaces shipped in Sprints 119–120: `/v1/vex/observations/{vulnerabilityId}/{productKey}` and `/v1/vex/evidence/chunks`. ## Scope and determinism - **Aggregation-only**: no consensus, severity merging, or reachability. Responses carry raw statements plus provenance/signature metadata. - **Stable ordering**: both endpoints sort by `lastSeen` DESC; pagination uses a deterministic `limit`. - **Limits**: observation projection default `limit=200`, max `500`; chunk stream default `limit=500`, max `2000`. - **Tenancy**: reads respect `X-Stella-Tenant` when provided; otherwise fall back to `DefaultTenant` configuration. - **Auth**: bearer token with `vex.read` scope required. ## `/v1/vex/observations/{vulnerabilityId}/{productKey}` - **Response**: JSON object with `vulnerabilityId`, `productKey`, `generatedAt`, `totalCount`, `truncated`, `statements[]`. - **Statement fields**: `observationId`, `providerId`, `status`, `justification`, `detail`, `firstSeen`, `lastSeen`, `scope{key,name,version,purl,cpe,componentIdentifiers[]}`, `anchors[]`, `document{digest,format,revision,sourceUri}`, `signature{type,keyId,issuer,verifiedAt}`. - **Filters**: - `providerId` (multi-valued, comma-separated) - `status` (values in `VexClaimStatus`) - `since` (ISO-8601, UTC) - `limit` (ints within bounds) - **Mapping back to storage**: - `observationId` = `{providerId}:{document.digest}` - `document.digest` locates the raw record in `vex_raw`. - `anchors` contain JSON pointers/paragraph locators from source metadata. Headers: - `Excititor-Results-Truncated: true|false` - `Excititor-Results-Total: ` ## `/v1/vex/evidence/chunks` - **Query params**: `vulnerabilityId` (required), `productKey` (required), optional `providerId`, `status`, `since`, `limit`. - **Response**: **NDJSON** stream; each line is a `VexEvidenceChunkResponse`. - **Chunk fields**: `observationId`, `linksetId`, `vulnerabilityId`, `productKey`, `providerId`, `status`, `justification`, `detail`, `scopeScore` (from confidence or signals), `firstSeen`, `lastSeen`, `scope{...}`, `document{digest,format,sourceUri,revision}`, `signature{type,subject,issuer,keyId,verifiedAt,transparencyRef}`, `metadata` (flattened additionalMetadata). - **Headers**: same truncation/total headers as projection API. - **Streaming guidance (SDK/clients)**: - Use HTTP client that supports response streaming; read line-by-line and JSON-deserialize per line. - Treat stream as unbounded list up to `limit`; do not assume array brackets. - Back-off or paginate by adjusting `since` or narrowing providers/statuses. ## `/v1/vex/attestations/{attestationId}` - **Purpose**: Lookup attestation provenance (supplier ↔ observation/linkset ↔ product/vulnerability) without touching consensus. - **Response**: `VexAttestationPayload` with fields: - `attestationId`, `supplierId`, `observationId`, `linksetId`, `vulnerabilityId`, `productKey`, `justificationSummary`, `issuedAt`, `metadata{}`. - **Semantics**: - `attestationId` matches the export/attestation ID used when signing (Resolve/Worker flows). - `observationId`/`linksetId` map back to evidence identifiers; clients can stitch provenance for citations. - **Auth**: `vex.read` scope; tenant header optional (payloads are tenant-agnostic). ## Error model - Standard API envelope with `ValidationProblem` for missing required params. - `scope` failures return `403` with problem details. - Tenancy parse failures return `400`. ## Backwards compatibility - No legacy routes are deprecated by these endpoints; they are additive and remain aggregation-only. ## References - Implementation: `src/Excititor/StellaOps.Excititor.WebService/Program.cs` (`/v1/vex/observations/**`, `/v1/vex/evidence/chunks`). - Telemetry: `src/Excititor/StellaOps.Excititor.WebService/Telemetry/EvidenceTelemetry.cs` (`excititor.vex.observation.*`, `excititor.vex.chunks.*`). - Data model: `src/Excititor/StellaOps.Excititor.WebService/Contracts/VexObservationContracts.cs`, `Contracts/VexEvidenceChunkContracts.cs`.