---
title: "Compliance Reports"
description: "Policies API v1 endpoints for compliance reports, history, and namespace compliance."
diataxis: reference
applies_to:
  product: "nirmata-control-hub"
audience: ["platform-engineer","developer"]
last_updated: 2026-10-06
url: https://docs.nirmata.io/docs/reference/rest-api/policies_api_v1/compliance_reports/
---


<!-- Generated by scripts/gen-policies-api-v1/gen.py. Edit inventory.json, not this file. -->

Read compliance reports per standard and target, drill into failing controls, export snapshots, view score history, and read namespace-level compliance.

All paths are relative to `/policies/api/v1`. See [Policies API v1](../) for authentication and conventions.

## Endpoints

| Operation | Method | Path |
|---|---|---|
| [Get compliance score or control history](#get-compliance-score-or-control-history) | `GET` | `/compliance/kyverno/history` |
| [Get namespace compliance settings](#get-namespace-compliance-settings) | `GET` | `/compliance/kyverno/namespace-config` |
| [Update namespace compliance settings](#update-namespace-compliance-settings) | `PUT` | `/compliance/kyverno/namespace-config` |
| [List current namespace compliance reports](#list-current-namespace-compliance-reports) | `GET` | `/compliance/kyverno/namespace-reports` |
| [List compliance reports](#list-compliance-reports) | `GET` | `/compliance/kyverno/reports` |
| [Export a compliance snapshot](#export-a-compliance-snapshot) | `GET` | `/compliance/kyverno/reports/export` |
| [List targets with compliance reports](#list-targets-with-compliance-reports) | `GET` | `/compliance/kyverno/reports/targets` |
| [Get a compliance report](#get-a-compliance-report) | `GET` | `/compliance/kyverno/reports/{id}` |
| [Get a namespace compliance snapshot](#get-a-namespace-compliance-snapshot) | `GET` | `/compliance/kyverno/reports/{reportId}/namespaces/{namespace}` |
| [List failing resources for a report control](#list-failing-resources-for-a-report-control) | `GET` | `/compliance/kyverno/reports/{id}/controls/{controlId}/findings` |
| [List namespace findings for a control](#list-namespace-findings-for-a-control) | `GET` | `/compliance/kyverno/reports/{reportId}/namespaces/{namespace}/controls/{controlId}/findings` |

## Reference

### Get compliance score or control history

```http
GET /policies/api/v1/compliance/kyverno/history
```

Without controlId, returns the score trend for a target and standard; points exist only when results changed, so charts should carry the last value forward. With controlId, returns one point per completed scan for that control. Range defaults to the last 90 days.

**Roles:** `admin`, `platform`, `security`, `devops`

**Query parameters**

| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| `targetId` | `string` | Yes | — | Target ID (UUID) |
| `standard` | `string` | Yes | — | Standard key |
| `controlId` | `string` | No | — | Return control-level history for this control instead of score history |
| `from` | `string` | No | — | ISO-8601 start; default 90 days before 'to' |
| `to` | `string` | No | — | ISO-8601 end; default now |

**Response** `200` (`application/json`)

`Array, ascending by time: score points {time, score, passControls, failControls, totalControls, level} or, with controlId, {time, controlId, status, passCount, failCount}`

| Field | Type | Description |
|---|---|---|
| `time` | `string (date-time)` | — |
| `score` | `number` | 0-100 (score history) |
| `passControls` | `integer` | — |
| `failControls` | `integer` | — |
| `totalControls` | `integer` | — |
| `level` | `string` | green \| yellow \| red \| unknown |
| `controlId` | `string` | (control history) |
| `status` | `string` | pass \| fail \| not_evaluated \| not_automatable (control history) |
| `passCount` | `integer` | (control history) |
| `failCount` | `integer` | (control history) |

**Errors**

- `400` — targetId/standard missing, targetId not a UUID, or invalid from/to
- `400` — Control history range exceeds the maximum points (default 5000; code CONTROL_HISTORY_RANGE_TOO_LARGE) - narrow from/to
- `401` — Not authenticated
- `403` — Caller's role is not in the allowed roles

### Get namespace compliance settings

```http
GET /policies/api/v1/compliance/kyverno/namespace-config
```

Returns the namespace compliance settings for a cluster. If none have been saved, returns a disabled configuration at revision 0.

**Roles:** `admin`, `security`

**Query parameters**

| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| `targetId` | `string` | Yes | — | Cluster ID (UUID) |

**Response** `200` (`application/json`)

NamespaceComplianceConfigDto

| Field | Type | Description |
|---|---|---|
| `targetId` | `string (uuid)` | Cluster ID |
| `featureEnabled` | `boolean` | Whether this deployment supports namespace compliance |
| `enabled` | `boolean` | — |
| `mode` | `string` | all \| specific (nullable) |
| `namespaces` | `string[]` | Selected namespaces (specific mode) |
| `revision` | `integer` | Use as expectedRevision on update |
| `createdBy` | `string` | — |
| `updatedBy` | `string` | — |
| `createdAt` | `string (date-time)` | — |
| `updatedAt` | `string (date-time)` | — |

**Errors**

- `400` — targetId not a UUID (INVALID_TARGET_ID)
- `404` — Cluster not found or inaccessible
- `401` — Not authenticated
- `403` — Caller's role is not in the allowed roles

### Update namespace compliance settings

```http
PUT /policies/api/v1/compliance/kyverno/namespace-config
```

Saves which namespaces of a cluster get namespace-level compliance views: all namespaces or a specific list. Uses optimistic concurrency: expectedRevision must equal the current revision (0 to create).

**Roles:** `admin`, `security`

**Query parameters**

| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| `targetId` | `string` | Yes | — | Cluster ID (UUID) |

**Request body** (`application/json`)

| Field | Type | Required | Description |
|---|---|---|---|
| `enabled` | `boolean` | Yes | — |
| `mode` | `string` | No | all \| specific; required when enabled or namespaces non-empty |
| `namespaces` | `string[]` | Yes | Valid Kubernetes namespace names; must be empty for all mode, non-empty for specific mode when enabled; use [] when none |
| `expectedRevision` | `integer` | Yes | Current revision from GET (&gt;=0) |

**Response** `200` (`application/json`)

NamespaceComplianceConfigDto

| Field | Type | Description |
|---|---|---|
| `targetId` | `string (uuid)` | Cluster ID |
| `featureEnabled` | `boolean` | Whether this deployment supports namespace compliance |
| `enabled` | `boolean` | — |
| `mode` | `string` | all \| specific (nullable) |
| `namespaces` | `string[]` | Selected namespaces (specific mode) |
| `revision` | `integer` | Use as expectedRevision on update |
| `createdBy` | `string` | — |
| `updatedBy` | `string` | — |
| `createdAt` | `string (date-time)` | — |
| `updatedAt` | `string (date-time)` | — |

**Errors**

- `400` — Invalid target, body, mode or namespace selection (e.g. ENABLED_REQUIRED, EXPECTED_REVISION_REQUIRED, NAMESPACES_REQUIRED, NAMESPACE_MODE_REQUIRED, NAMESPACES_NOT_ALLOWED, INVALID_NAMESPACE)
- `404` — Cluster not found or inaccessible
- `409` — Revision mismatch (CONFIG_REVISION_CONFLICT) or feature not available in this deployment (NAMESPACE_COMPLIANCE_UNAVAILABLE)
- `401` — Not authenticated
- `403` — Caller's role is not in the allowed roles

**Example**

```json
{"enabled":true,"mode":"specific","namespaces":["payments","web"],"expectedRevision":0}
```

### List current namespace compliance reports

```http
GET /policies/api/v1/compliance/kyverno/namespace-reports
```

For a cluster and namespace, returns the latest completed report per standard projected onto that namespace, along with the current scope state. Never falls back to an older snapshot; if the namespace is not selected or data is pending, availability explains why.

**Roles:** `admin`, `platform`, `security`, `devops`

**Query parameters**

| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| `targetId` | `string` | Yes | — | Cluster ID (UUID) |
| `namespace` | `string` | Yes | — | Kubernetes namespace |
| `standard` | `string` | No | — | Filter by standard key |
| `page` | `integer` | No | `0` | 0-based page |
| `size` | `integer` | No | `50` | Page size, clamped to 1-100 |

**Response** `200` (`application/json`)

NamespaceComplianceReportsResponseDto

| Field | Type | Description |
|---|---|---|
| `targetId` | `string (uuid)` | — |
| `namespace` | `string` | — |
| `featureEnabled` | `boolean` | — |
| `enabled` | `boolean` | — |
| `mode` | `string` | all \| specific |
| `selectedNamespaces` | `string[]` | Empty unless caller has admin/platform/security role |
| `configRevision` | `integer` | — |
| `availability` | `string` | disabled \| not_selected \| pending \| available \| no_data \| projection_unavailable \| unsupported_scope |
| `reason` | `string` | — |
| `reports` | `object` | Page: {content: T[], totalElements, totalPages, number (0-based page), size} of {parentReportId, targetId, namespace, standard, scanTimestamp, projectionVersion, availability, reason, summary: {score (nullable 0-100), passCount, failCount, totalCount, notEvaluatedCount, notAutomatableCount, level}} |

**Errors**

- `400` — Invalid targetId or namespace
- `404` — Cluster or namespace not found or inaccessible
- `401` — Not authenticated
- `403` — Caller's role is not in the allowed roles

### List compliance reports

```http
GET /policies/api/v1/compliance/kyverno/reports
```

Lists completed compliance reports. By default (latest=true) returns the most recent completed report for each target and standard combination as an unpaginated array; with latest=false returns all completed reports, newest first, paginated.

**Roles:** `admin`, `platform`, `security`, `devops`

**Query parameters**

| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| `targetId` | `string` | No | — | Filter by target ID (UUID) |
| `targetType` | `string` | No | — | Filter by target type, e.g. cluster |
| `standard` | `string` | No | — | Filter by standard key |
| `level` | `string` | No | — | Filter by level: green \| yellow \| red \| unknown |
| `latest` | `boolean` | No | `true` | Return only the latest report per target and standard |
| `page` | `integer` | No | `0` | 0-based page (latest=false only) |
| `size` | `integer` | No | `50` | Page size, clamped to 1-100 (latest=false only) |

**Response** `200` (`application/json`)

`latest=true: ComplianceReportDto[]; latest=false: Page: {content: T[], totalElements, totalPages, number (0-based page), size} of ComplianceReportDto`

| Field | Type | Description |
|---|---|---|
| `id` | `string (uuid)` | Report ID |
| `targetId` | `string (uuid)` | Scanned target (cluster/repository) ID |
| `targetType` | `string` | Target type, e.g. cluster, repository |
| `targetName` | `string` | Target display name |
| `standard` | `string` | Standard key, e.g. soc2 |
| `standardDisplayName` | `string` | Human-readable standard name |
| `scanTimestamp` | `string (date-time)` | When the scan ran |
| `status` | `string` | Report status (lists only return completed) |
| `triggerType` | `string` | scheduled \| on_demand \| published |
| `summary` | `object` | {score (0-100, null if nothing evaluated), passCount, failCount, totalCount, notEvaluatedCount, notAutomatableCount, level: green\|yellow\|red\|unknown} |

**Errors**

- `400` — targetId is not a valid UUID
- `401` — Not authenticated
- `403` — Caller's role is not in the allowed roles

### Export a compliance snapshot

```http
GET /policies/api/v1/compliance/kyverno/reports/export
```

Returns the most recent completed report for a target and standard at or before asOf, in the nctl snapshot JSON format (snake_case control fields) so it can be used with `nctl compliance diff`. Includes per-control findings and structured exception details.

**Roles:** `admin`, `platform`, `security`, `devops`

**Query parameters**

| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| `targetId` | `string` | Yes | — | Target ID (UUID) |
| `standard` | `string` | Yes | — | Standard key |
| `asOf` | `string` | No | — | ISO-8601 date-time with offset; defaults to now |

**Response** `200` (`application/json`)

nctl-compatible snapshot

| Field | Type | Description |
|---|---|---|
| `id` | `string` | Snapshot ID in yyyyMMdd-HHmmss form |
| `standard` | `string` | — |
| `target` | `string` | Target name |
| `targetType` | `string` | — |
| `timestamp` | `string (date-time)` | Scan time |
| `exceptions` | `string[]` | Policy exceptions in effect |
| `exceptionDetails` | `object[]` | {name, namespace, policies[], managed, requestedBy, approvedBy, reason, state, startTime, expiryTime, history[]} |
| `controls` | `object[]` | {id, policy, policy_count, status, pass_count, fail_count, findings[]} |

**Errors**

- `400` — targetId/standard missing, targetId not a UUID, or invalid asOf
- `404` — No completed snapshot at or before asOf
- `401` — Not authenticated
- `403` — Caller's role is not in the allowed roles

### List targets with compliance reports

```http
GET /policies/api/v1/compliance/kyverno/reports/targets
```

Returns each target that has at least one completed compliance report, with its most recent name, ordered by type and name. Useful for populating target pickers.

**Roles:** `admin`, `platform`, `security`, `devops`

**Response** `200` (`application/json`)

`Array of {targetId, targetName, targetType}`

| Field | Type | Description |
|---|---|---|
| `targetId` | `string (uuid)` | — |
| `targetName` | `string` | — |
| `targetType` | `string` | cluster \| repository \| cloud_account |

**Errors**

- `401` — Not authenticated
- `403` — Caller's role is not in the allowed roles

### Get a compliance report

```http
GET /policies/api/v1/compliance/kyverno/reports/{id}
```

Returns one compliance report with its per-control results. For failed scans, errorMessage explains the failure.

**Roles:** `admin`, `platform`, `security`, `devops`

**Path parameters**

| Name | Type | Description |
|---|---|---|
| `id` | `string` | Report ID (UUID) |

**Response** `200` (`application/json`)

`ComplianceReportDto with controls[]`

| Field | Type | Description |
|---|---|---|
| `id` | `string (uuid)` | Report ID |
| `targetId` | `string (uuid)` | Scanned target (cluster/repository) ID |
| `targetType` | `string` | Target type, e.g. cluster, repository |
| `targetName` | `string` | Target display name |
| `standard` | `string` | Standard key, e.g. soc2 |
| `standardDisplayName` | `string` | Human-readable standard name |
| `scanTimestamp` | `string (date-time)` | When the scan ran |
| `status` | `string` | Report status (lists only return completed) |
| `triggerType` | `string` | scheduled \| on_demand \| published |
| `summary` | `object` | {score (0-100, null if nothing evaluated), passCount, failCount, totalCount, notEvaluatedCount, notAutomatableCount, level: green\|yellow\|red\|unknown} |
| `errorMessage` | `string` | Set when status is failed |
| `controls` | `object[]` | {controlId, status: pass\|fail\|not_evaluated\|not_automatable, passCount, failCount, policiesTotal, policiesEvaluated, primaryPolicy, failingPolicies[], allPolicies[], notAutomatableReason} |

**Errors**

- `400` — id is not a valid UUID
- `404` — Report not found in this tenant (empty body)
- `401` — Not authenticated
- `403` — Caller's role is not in the allowed roles

### Get a namespace compliance snapshot

```http
GET /policies/api/v1/compliance/kyverno/reports/{reportId}/namespaces/{namespace}
```

Returns the immutable namespace-level view of a cluster compliance report, including per-control results. If no namespace data exists for that report, availability and reason explain why.

**Roles:** `admin`, `platform`, `security`, `devops`

**Path parameters**

| Name | Type | Description |
|---|---|---|
| `reportId` | `string` | Parent cluster report ID (UUID) |
| `namespace` | `string` | Kubernetes namespace |

**Response** `200` (`application/json`)

NamespaceComplianceReportDetailDto

| Field | Type | Description |
|---|---|---|
| `parentReportId` | `string (uuid)` | — |
| `targetId` | `string (uuid)` | — |
| `namespace` | `string` | — |
| `standard` | `string` | — |
| `scanTimestamp` | `string (date-time)` | — |
| `projectionVersion` | `integer` | — |
| `configRevision` | `integer` | — |
| `selectionMode` | `string` | all \| specific |
| `selectedNamespaces` | `string[]` | Empty unless caller has cluster-wide access |
| `availability` | `string` | disabled \| not_selected \| pending \| available \| no_data \| projection_unavailable \| unsupported_scope |
| `reason` | `string` | — |
| `summary` | `object` | {score (nullable 0-100), passCount, failCount, totalCount, notEvaluatedCount, notAutomatableCount, level} |
| `controls` | `object[]` | {controlId, status: pass\|fail\|not_evaluated\|not_automatable, passCount, failCount, policiesTotal, policiesEvaluated, primaryPolicy, failingPolicies[], allPolicies[], notAutomatableReason} |

**Errors**

- `400` — Invalid reportId (INVALID_REPORT_ID) or namespace (INVALID_NAMESPACE)
- `404` — Report, cluster or namespace not found or inaccessible
- `500` — Stored snapshot is invalid (NAMESPACE_SNAPSHOT_CORRUPT)
- `401` — Not authenticated
- `403` — Caller's role is not in the allowed roles

### List failing resources for a report control

```http
GET /policies/api/v1/compliance/kyverno/reports/{id}/controls/{controlId}/findings
```

Returns a paginated list of the failing or warning resources captured for one control of a report, ordered by namespace, kind and name. Returns an empty page if no evidence was captured for the control (including older reports).

**Roles:** `admin`, `platform`, `security`, `devops`

**Path parameters**

| Name | Type | Description |
|---|---|---|
| `id` | `string` | Report ID (UUID) |
| `controlId` | `string` | Control ID within the standard, e.g. CC6.1 |

**Query parameters**

| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| `page` | `integer` | No | `0` | 0-based page |
| `size` | `integer` | No | `50` | Page size, clamped to 1-100 |

**Response** `200` (`application/json`)

`Page: {content: T[], totalElements, totalPages, number (0-based page), size} of findings`

| Field | Type | Description |
|---|---|---|
| `policyName` | `string` | Kyverno policy |
| `ruleName` | `string` | Policy rule |
| `resourceKind` | `string` | Resource kind |
| `resourceName` | `string` | Resource name |
| `resourceNamespace` | `string` | Resource namespace |
| `message` | `string` | Policy result message |
| `result` | `string` | Policy result, e.g. fail or warn |

**Errors**

- `400` — id is not a valid UUID (code INVALID_ID)
- `404` — Report not found in this tenant (code REPORT_NOT_FOUND)
- `401` — Not authenticated
- `403` — Caller's role is not in the allowed roles

### List namespace findings for a control

```http
GET /policies/api/v1/compliance/kyverno/reports/{reportId}/namespaces/{namespace}/controls/{controlId}/findings
```

Returns a page of the retained failing-resource evidence for one control in a namespace snapshot. Evidence is sampled (up to 50 per control); totalElements counts retained findings and aggregateFailCount gives the true failure count.

**Roles:** `admin`, `platform`, `security`, `devops`

**Path parameters**

| Name | Type | Description |
|---|---|---|
| `reportId` | `string` | Parent report ID (UUID) |
| `namespace` | `string` | Kubernetes namespace |
| `controlId` | `string` | Control ID |

**Query parameters**

| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| `page` | `integer` | No | `0` | 0-based page |
| `size` | `integer` | No | `50` | Page size, clamped to 1-100 |

**Response** `200` (`application/json`)

`{content: Finding[], totalElements, totalPages, number, size, sampleLimit, sampled, aggregateFailCount}`

| Field | Type | Description |
|---|---|---|
| `policyName` | `string` | Kyverno policy |
| `ruleName` | `string` | Policy rule |
| `resourceKind` | `string` | Resource kind |
| `resourceName` | `string` | Resource name |
| `resourceNamespace` | `string` | Resource namespace |
| `message` | `string` | Policy result message |
| `result` | `string` | Policy result, e.g. fail or warn |
| `sampleLimit` | `integer` | Max findings retained per control (50) |
| `sampled` | `boolean` | True when failures exceed retained evidence |
| `aggregateFailCount` | `integer` | Total failing evaluations |

**Errors**

- `400` — Invalid reportId, namespace, or blank controlId
- `404` — Report/namespace not found or inaccessible, or CONTROL_NOT_FOUND
- `409` — No available namespace snapshot for this report (NAMESPACE_SNAPSHOT_UNAVAILABLE)
- `500` — NAMESPACE_SNAPSHOT_CORRUPT
- `401` — Not authenticated
- `403` — Caller's role is not in the allowed roles


