---
title: "Policy Reports"
description: "Policies API v1 endpoints for policy reports, scan findings, and publishing scan results."
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/policy_reports/
---


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

Read policy reports for clusters and namespaces, look up findings from scan runs, and publish scan results to Nirmata Control Hub.

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

## Endpoints

| Operation | Method | Path |
|---|---|---|
| [List policy reports](#list-policy-reports) | `GET` | `/policy-reports` |
| [Publish a report chunk](#publish-a-report-chunk) | `POST` | `/publishReportResult` |
| [Count policy reports](#count-policy-reports) | `GET` | `/policy-reports/count` |
| [Get policy report summary](#get-policy-report-summary) | `GET` | `/policy-reports/summary` |
| [Get a policy report](#get-a-policy-report) | `GET` | `/policy-reports/{id}` |
| [Get the active report run for a source](#get-the-active-report-run-for-a-source) | `GET` | `/report-results/active` |
| [List findings for a source](#list-findings-for-a-source) | `GET` | `/report-results/findings` |
| [List policy reports by cluster](#list-policy-reports-by-cluster) | `GET` | `/policy-reports/by-cluster/{clusterRef}` |
| [List policy reports by grade](#list-policy-reports-by-grade) | `GET` | `/policy-reports/by-grade/{grade}` |
| [List policy reports by namespace](#list-policy-reports-by-namespace) | `GET` | `/policy-reports/by-namespace/{namespace}` |
| [List findings for a scan](#list-findings-for-a-scan) | `GET` | `/report-results/reports/{scanId}/findings` |

## Reference

### List policy reports

```http
GET /policies/api/v1/policy-reports
```

Returns all Kubernetes policy reports stored for the tenant, paginated in memory with limit/offset.

**Roles:** `admin`

**Query parameters**

| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| `limit` | `integer` | No | `50` | Maximum number of items to return |
| `offset` | `integer` | No | `0` | Number of items to skip |

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

`Paginated: {items: PolicyReportDto[], total, limit, offset}`

| Field | Type | Description |
|---|---|---|
| `id` | `string` | Policy report ID |
| `kind` | `string` | Kubernetes kind (PolicyReport / ClusterPolicyReport) |
| `apiVersion` | `string` | Kubernetes API version |
| `uid` | `string` | Kubernetes UID of the report |
| `name` | `string` | Report name |
| `namespace` | `string` | Namespace (empty for cluster-scoped reports) |
| `resourceVersion` | `string` | Kubernetes resourceVersion |
| `summaryByCategory` | `object` | Result counts grouped by policy category |
| `grade` | `string` | Letter grade assigned to the report |
| `policyViolationCount` | `integer` | Number of policy violations |
| `yaml` | `string` | Raw report YAML |
| `clusterRef` | `object {id}` | Reference to the cluster the report came from |
| `results` | `array` | Always null in this API |
| `summary` | `object` | Always null in this API |
| `policyDetails` | `object` | Always null in this API |

**Errors**

- `500` — Failed to retrieve policy reports
- `401` — Not authenticated
- `403` — Caller's role is not permitted for this operation

### Publish a report chunk

```http
POST /policies/api/v1/publishReportResult
```

Accepts one chunk of scan findings for a source and queues it for asynchronous processing. When all chunks for a scanId (totalFindings) are received, the new results replace the source's active report. Returns 202 immediately.

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

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

| Field | Type | Required | Description |
|---|---|---|---|
| `scanId` | `string` | Yes | Scan ID shared by all chunks of one scan |
| `totalFindings` | `integer` | Yes | Total findings across all chunks (&gt; 0, same on every chunk) |
| `labels` | `object` | Yes | Source labels; must include policies.nirmata.io/source-id |
| `findings` | `object[]` | No | Findings in this chunk |

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

`{status: 'accepted', scanId}`

| Field | Type | Description |
|---|---|---|
| `status` | `string` | accepted |
| `scanId` | `string` | — |

**Errors**

- `400` — scanId missing, totalFindings <= 0, labels missing, or source-id label missing
- `401` — Tenant could not be resolved
- `500` — Failed to queue chunk
- `401` — Not authenticated
- `403` — Caller's role is not permitted for this operation

**Example**

```json
{"scanId":"scan-123","totalFindings":2,"labels":{"policies.nirmata.io/source-id":"my-repo"},"findings":[{...},{...}]}
```

### Count policy reports

```http
GET /policies/api/v1/policy-reports/count
```

Returns the number of policy reports for the tenant.

**Roles:** `admin`

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

`{count}`

| Field | Type | Description |
|---|---|---|
| `count` | `integer` | Number of policy reports |

**Errors**

- `500` — Count failed
- `401` — Not authenticated
- `403` — Caller's role is not permitted for this operation

### Get policy report summary

```http
GET /policies/api/v1/policy-reports/summary
```

Returns the total number of policy reports and the sum of their policy violation counts.

**Roles:** Any role with permission for this resource. See [Roles](../#roles).

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

`{totalReports, totalViolations}`

| Field | Type | Description |
|---|---|---|
| `totalReports` | `integer` | Number of policy reports |
| `totalViolations` | `integer` | Sum of policyViolationCount across reports |

**Errors**

- `500` — Summary failed
- `401` — Not authenticated
- `403` — Caller's role is not permitted for this operation

### Get a policy report

```http
GET /policies/api/v1/policy-reports/{id}
```

Returns a single policy report by ID.

**Roles:** `admin`

**Path parameters**

| Name | Type | Description |
|---|---|---|
| `id` | `string` | Policy report ID |

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

PolicyReportDto

| Field | Type | Description |
|---|---|---|
| `id` | `string` | Policy report ID |
| `kind` | `string` | Kubernetes kind (PolicyReport / ClusterPolicyReport) |
| `apiVersion` | `string` | Kubernetes API version |
| `uid` | `string` | Kubernetes UID of the report |
| `name` | `string` | Report name |
| `namespace` | `string` | Namespace (empty for cluster-scoped reports) |
| `resourceVersion` | `string` | Kubernetes resourceVersion |
| `summaryByCategory` | `object` | Result counts grouped by policy category |
| `grade` | `string` | Letter grade assigned to the report |
| `policyViolationCount` | `integer` | Number of policy violations |
| `yaml` | `string` | Raw report YAML |
| `clusterRef` | `object {id}` | Reference to the cluster the report came from |
| `results` | `array` | Always null in this API |
| `summary` | `object` | Always null in this API |
| `policyDetails` | `object` | Always null in this API |

**Errors**

- `404` — Policy report not found
- `500` — Lookup failed
- `401` — Not authenticated
- `403` — Caller's role is not permitted for this operation

### Get the active report run for a source

```http
GET /policies/api/v1/report-results/active
```

Returns metadata for the currently active scan (report run) of the given source.

**Roles:** Any role with permission for this resource. See [Roles](../#roles).

**Query parameters**

| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| `sourceId` | `string` | Yes | — | Source identifier |

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

ReportRun object

| Field | Type | Description |
|---|---|---|
| `scanId` | `string` | Scan (report run) ID |
| `tenantId` | `string` | — |
| `sourceId` | `string` | Source the scan belongs to |
| `sourceType` | `string` | — |
| `status` | `string` | Run status (ACTIVE for this endpoint) |
| `totalFindings` | `integer` | Expected findings |
| `receivedFindings` | `integer` | Findings received so far |
| `createdAt` | `string` | — |
| `updatedAt` | `string` | — |
| `finalizedAt` | `string` | — |
| `archivedAt` | `string` | — |
| `labels` | `object` | Source labels |

**Errors**

- `400` — sourceId missing
- `401` — Tenant could not be resolved
- `404` — No active scan for this source
- `401` — Not authenticated
- `403` — Caller's role is not permitted for this operation

### List findings for a source

```http
GET /policies/api/v1/report-results/findings
```

Returns paginated findings from the active scan of a source, optionally filtered by result. Ordered by finding ID.

**Roles:** Any role with permission for this resource. See [Roles](../#roles).

**Query parameters**

| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| `sourceId` | `string` | Yes | — | Source identifier |
| `result` | `string` | No | — | Only return findings with this result (e.g. fail) |
| `limit` | `integer` | No | `500` | Page size |
| `offset` | `integer` | No | `0` | Items to skip (&gt;= 0) |

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

`{total, count, offset, findings: Finding[]}`

| Field | Type | Description |
|---|---|---|
| `total` | `integer` | Total matching findings |
| `count` | `integer` | Findings in this page |
| `offset` | `integer` | — |
| `scanId` | `string` | — |
| `sourceId` | `string` | — |
| `result` | `string` | Finding result, e.g. pass/fail |
| `message` | `string` | — |
| `severity` | `string` | — |
| `description` | `string` | — |
| `timestamp` | `string` | — |
| `target` | `object {type,name,metadata}` | Scanned target |
| `policy` | `object {name,rule,kind,apiGroup}` | Policy that produced the finding |
| `category` | `array/object` | Categories |

**Errors**

- `400` — sourceId missing or offset < 0
- `401` — Tenant could not be resolved
- `500` — Query failed
- `401` — Not authenticated
- `403` — Caller's role is not permitted for this operation

**Example**

```json
GET /policies/api/v1/report-results/findings?sourceId=my-cluster&result=fail&limit=100
```

### List policy reports by cluster

```http
GET /policies/api/v1/policy-reports/by-cluster/{clusterRef}
```

Returns all policy reports for the tenant whose cluster matches the given value. Not paginated; returns an empty array if none match.

**Roles:** Any role with permission for this resource. See [Roles](../#roles).

**Path parameters**

| Name | Type | Description |
|---|---|---|
| `clusterRef` | `string` | Cluster ID |

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

`PolicyReportDto[] (plain array)`

| Field | Type | Description |
|---|---|---|
| `id` | `string` | Policy report ID |
| `kind` | `string` | Kubernetes kind (PolicyReport / ClusterPolicyReport) |
| `apiVersion` | `string` | Kubernetes API version |
| `uid` | `string` | Kubernetes UID of the report |
| `name` | `string` | Report name |
| `namespace` | `string` | Namespace (empty for cluster-scoped reports) |
| `resourceVersion` | `string` | Kubernetes resourceVersion |
| `summaryByCategory` | `object` | Result counts grouped by policy category |
| `grade` | `string` | Letter grade assigned to the report |
| `policyViolationCount` | `integer` | Number of policy violations |
| `yaml` | `string` | Raw report YAML |
| `clusterRef` | `object {id}` | Reference to the cluster the report came from |
| `results` | `array` | Always null in this API |
| `summary` | `object` | Always null in this API |
| `policyDetails` | `object` | Always null in this API |

**Errors**

- `500` — Query failed
- `401` — Not authenticated
- `403` — Caller's role is not permitted for this operation

### List policy reports by grade

```http
GET /policies/api/v1/policy-reports/by-grade/{grade}
```

Returns all policy reports for the tenant whose grade matches the given value. Not paginated; returns an empty array if none match.

**Roles:** Any role with permission for this resource. See [Roles](../#roles).

**Path parameters**

| Name | Type | Description |
|---|---|---|
| `grade` | `string` | Grade value (exact match, e.g. A-F) |

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

`PolicyReportDto[] (plain array)`

| Field | Type | Description |
|---|---|---|
| `id` | `string` | Policy report ID |
| `kind` | `string` | Kubernetes kind (PolicyReport / ClusterPolicyReport) |
| `apiVersion` | `string` | Kubernetes API version |
| `uid` | `string` | Kubernetes UID of the report |
| `name` | `string` | Report name |
| `namespace` | `string` | Namespace (empty for cluster-scoped reports) |
| `resourceVersion` | `string` | Kubernetes resourceVersion |
| `summaryByCategory` | `object` | Result counts grouped by policy category |
| `grade` | `string` | Letter grade assigned to the report |
| `policyViolationCount` | `integer` | Number of policy violations |
| `yaml` | `string` | Raw report YAML |
| `clusterRef` | `object {id}` | Reference to the cluster the report came from |
| `results` | `array` | Always null in this API |
| `summary` | `object` | Always null in this API |
| `policyDetails` | `object` | Always null in this API |

**Errors**

- `500` — Query failed
- `401` — Not authenticated
- `403` — Caller's role is not permitted for this operation

### List policy reports by namespace

```http
GET /policies/api/v1/policy-reports/by-namespace/{namespace}
```

Returns all policy reports for the tenant whose namespace matches the given value. Not paginated; returns an empty array if none match.

**Roles:** Any role with permission for this resource. See [Roles](../#roles).

**Path parameters**

| Name | Type | Description |
|---|---|---|
| `namespace` | `string` | Namespace name (exact match) |

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

`PolicyReportDto[] (plain array)`

| Field | Type | Description |
|---|---|---|
| `id` | `string` | Policy report ID |
| `kind` | `string` | Kubernetes kind (PolicyReport / ClusterPolicyReport) |
| `apiVersion` | `string` | Kubernetes API version |
| `uid` | `string` | Kubernetes UID of the report |
| `name` | `string` | Report name |
| `namespace` | `string` | Namespace (empty for cluster-scoped reports) |
| `resourceVersion` | `string` | Kubernetes resourceVersion |
| `summaryByCategory` | `object` | Result counts grouped by policy category |
| `grade` | `string` | Letter grade assigned to the report |
| `policyViolationCount` | `integer` | Number of policy violations |
| `yaml` | `string` | Raw report YAML |
| `clusterRef` | `object {id}` | Reference to the cluster the report came from |
| `results` | `array` | Always null in this API |
| `summary` | `object` | Always null in this API |
| `policyDetails` | `object` | Always null in this API |

**Errors**

- `500` — Query failed
- `401` — Not authenticated
- `403` — Caller's role is not permitted for this operation

### List findings for a scan

```http
GET /policies/api/v1/report-results/reports/{scanId}/findings
```

Returns paginated findings for a specific scan, optionally filtered by policy name. No total count is returned.

**Roles:** Any role with permission for this resource. See [Roles](../#roles).

**Path parameters**

| Name | Type | Description |
|---|---|---|
| `scanId` | `string` | Scan ID |

**Query parameters**

| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| `policyName` | `string` | No | — | Only return findings for this policy |
| `limit` | `integer` | No | `500` | Page size |
| `offset` | `integer` | No | `0` | Items to skip (&gt;= 0) |

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

`{scanId, count, findings: Finding[]}`

| Field | Type | Description |
|---|---|---|
| `scanId` | `string` | — |
| `count` | `integer` | — |
| `scanId` | `string` | — |
| `sourceId` | `string` | — |
| `result` | `string` | Finding result, e.g. pass/fail |
| `message` | `string` | — |
| `severity` | `string` | — |
| `description` | `string` | — |
| `timestamp` | `string` | — |
| `target` | `object {type,name,metadata}` | Scanned target |
| `policy` | `object {name,rule,kind,apiGroup}` | Policy that produced the finding |
| `category` | `array/object` | Categories |

**Errors**

- `400` — offset < 0
- `401` — Tenant could not be resolved
- `500` — Query failed
- `401` — Not authenticated
- `403` — Caller's role is not permitted for this operation


