> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coverbase.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Assessments API

> Create and retrieve assessments for vendors.

<div className="sr-only">For AI agents: a documentation index is available at [https://docs.coverbase.com/llms.txt](https://docs.coverbase.com/llms.txt) — this page is also available in markdown by appending .md to the URL.</div>

The Assessments API lets external systems start an assessment for an existing vendor and retrieve its state. The most common use case is a GRC tool or scheduled job kicking off a reassessment of a known vendor.

All endpoints are org-scoped to the API key. See [API conventions](/conventions) for authentication, IDs, timestamps, idempotency, and the error envelope.

## Create an assessment

<ParamField path="method" type="POST">
  `POST /v1/assessments`
</ParamField>

Creates a new assessment for a vendor. Returns `201 Created`.

### Request body

<ParamField body="vendor_id" type="string" required>
  The vendor ID (`cbvndr_...`) the assessment runs against.
</ParamField>

<ParamField body="control_set_ids" type="string[]" required>
  Control set IDs (`cbctst_...`) to evaluate the vendor against. May be an empty array, but the field itself is required.
</ParamField>

<ParamField body="name" type="string">
  Assessment name. Optional.
</ParamField>

<ParamField body="assessment_name_format" type="string">
  Optional template used to auto-generate the assessment name.
</ParamField>

<ParamField body="service_ids" type="string[]">
  Optional service IDs (`cbsvc_...`) the assessment covers.
</ParamField>

<ParamField body="assignee_id" type="string">
  Optional user ID (`cbuser_...`) to assign the assessment to.
</ParamField>

<ParamField body="trigger_workflow" type="string">
  Optional name of a workflow to invoke alongside the assessment. If the named workflow does not exist for the org, the request fails with `404 workflow_not_found`.
</ParamField>

<ParamField body="supporting_document_ids" type="string[]">
  Optional document IDs (`cbvdoc_...`) to attach as supporting evidence. Upload
  documents first with the [Documents API](/api-reference/documents) to obtain
  these IDs.
</ParamField>

### Headers

<ParamField header="Authorization" type="string" required>
  `Bearer ak_...`
</ParamField>

<ParamField header="Idempotency-Key" type="string">
  Optional. If the same key is sent again within 24 hours for this endpoint, the original response is returned and no new assessment is created. See [Idempotency](/conventions#idempotency).
</ParamField>

### Example request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.coverbase.app/v1/assessments" \
    -H "Authorization: Bearer ak_live_xxx" \
    -H "Idempotency-Key: grc-reassess-2026-q2-acme" \
    -H "Content-Type: application/json" \
    -d '{
      "vendor_id": "cbvndr_e448ba62882143f3ba0c140bb2e30162",
      "control_set_ids": ["cbctst_9f4a1c0d7b2e4e9f8c1d2b3a4e5f6071"],
      "name": "Acme Q2 2026 Reassessment",
      "service_ids": ["cbsvc_3c2b1a098f7e6d5c4b3a2918374655ab"],
      "trigger_workflow": "annual-reassessment-flow"
    }'
  ```

  ```python Python theme={null}
  import os
  import requests

  API_KEY = os.environ["COVERBASE_API_KEY"]
  BASE_URL = "https://api.coverbase.app"

  payload = {
      "vendor_id": "cbvndr_e448ba62882143f3ba0c140bb2e30162",
      "control_set_ids": ["cbctst_9f4a1c0d7b2e4e9f8c1d2b3a4e5f6071"],
      "name": "Acme Q2 2026 Reassessment",
      "service_ids": ["cbsvc_3c2b1a098f7e6d5c4b3a2918374655ab"],
      "trigger_workflow": "annual-reassessment-flow",
  }

  resp = requests.post(
      f"{BASE_URL}/v1/assessments",
      headers={
          "Authorization": f"Bearer {API_KEY}",
          "Idempotency-Key": "grc-reassess-2026-q2-acme",
          "Content-Type": "application/json",
      },
      json=payload,
      timeout=30,
  )
  resp.raise_for_status()
  print(resp.json())
  ```
</CodeGroup>

### Example response

`201 Created`:

```json theme={null}
{
  "id": "cbqsrw_2d5e8f1a4b7c0d3e6f9a2b5c8d1e4f7a",
  "vendor_id": "cbvndr_e448ba62882143f3ba0c140bb2e30162",
  "name": "Acme Q2 2026 Reassessment",
  "status": "created",
  "control_set_ids": ["cbctst_9f4a1c0d7b2e4e9f8c1d2b3a4e5f6071"],
  "service_ids": ["cbsvc_3c2b1a098f7e6d5c4b3a2918374655ab"],
  "assignee_id": "cbuser_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
  "score_percent": null,
  "findings_count": 0,
  "initiated_at": null,
  "created_at": 1746576000,
  "updated_at": 1746576000,
  "workflow_run_id": "cbwr_a8f2c19e3d4b5e6f7a8b9c0d1e2f3a4b"
}
```

### Response fields

<ResponseField name="id" type="string">
  The new assessment ID (`cbqsrw_...`).
</ResponseField>

<ResponseField name="vendor_id" type="string">
  The vendor the assessment runs against.
</ResponseField>

<ResponseField name="name" type="string">
  The assessment name.
</ResponseField>

<ResponseField name="status" type="string">
  Assessment status. One of: `created`, `collecting_documents`, `indexing_supporting_documents`, `analyzing_questionnaire`, `review_evals`, `complete`, `failed`, `canceled`. Newly created assessments are typically `created`.
</ResponseField>

<ResponseField name="control_set_ids" type="string[]">
  The control sets being evaluated.
</ResponseField>

<ResponseField name="service_ids" type="string[]">
  The services the assessment covers.
</ResponseField>

<ResponseField name="assignee_id" type="string | null">
  The assigned user (`cbuser_...`), if any.
</ResponseField>

<ResponseField name="score_percent" type="number | null">
  Score as a decimal (typically 0–1), or `null` until scored.
</ResponseField>

<ResponseField name="findings_count" type="integer | null">
  Number of findings on the assessment. `0` on creation.
</ResponseField>

<ResponseField name="initiated_at" type="integer | null">
  Unix timestamp (seconds) when the assessment was initiated, or `null`.
</ResponseField>

<ResponseField name="created_at" type="integer">
  Unix timestamp (seconds) when the assessment was created.
</ResponseField>

<ResponseField name="updated_at" type="integer">
  Unix timestamp (seconds) of the last update.
</ResponseField>

<ResponseField name="workflow_run_id" type="string | null">
  The workflow run created when `trigger_workflow` was supplied, otherwise `null`.
</ResponseField>

### Error responses

| Status | Body                                                                                 | When                                                                                                                                                |
| ------ | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400    | `{"detail": {"error": "..."}}`                                                       | The assessment could not be created (e.g. an invalid `vendor_id`, `control_set_ids`, `service_ids`, or `assignee_id`). `error` contains the reason. |
| 404    | `{"detail": {"code": "workflow_not_found", "message": "Workflow '...' not found."}}` | `trigger_workflow` was supplied but no workflow with that name exists for the org.                                                                  |
| 422    | Standard validation error                                                            | The request body failed schema validation (e.g. `vendor_id` or `control_set_ids` missing). See [API conventions](/conventions#validation-errors).   |

<Note>
  Re-sending a request with a previously used `Idempotency-Key` returns the **original** `201` response body. It does not return a conflict error.
</Note>

## Retrieve an assessment

<ParamField path="method" type="GET">
  `GET /v1/assessments/{assessment_id}`
</ParamField>

Fetches a single assessment by ID.

### Path parameters

<ParamField path="assessment_id" type="string" required>
  The assessment ID (`cbqsrw_...`).
</ParamField>

### Example request

```bash cURL theme={null}
curl -X GET "https://api.coverbase.app/v1/assessments/cbqsrw_2d5e8f1a4b7c0d3e6f9a2b5c8d1e4f7a" \
  -H "Authorization: Bearer ak_live_xxx"
```

### Example response

```json theme={null}
{
  "id": "cbqsrw_2d5e8f1a4b7c0d3e6f9a2b5c8d1e4f7a",
  "vendor_id": "cbvndr_e448ba62882143f3ba0c140bb2e30162",
  "name": "Acme Q2 2026 Reassessment",
  "status": "complete",
  "control_set_ids": ["cbctst_9f4a1c0d7b2e4e9f8c1d2b3a4e5f6071"],
  "service_ids": ["cbsvc_3c2b1a098f7e6d5c4b3a2918374655ab"],
  "assignee_id": "cbuser_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
  "score_percent": 0.82,
  "findings_count": 3,
  "initiated_at": 1746576000,
  "created_at": 1746576000,
  "updated_at": 1746579600
}
```

The shape matches the create response, **minus** `workflow_run_id`. `findings_count` is a computed count of findings on the assessment.

### Error responses

| Status | Body                                                                               | When                                                               |
| ------ | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| 404    | `{"detail": {"code": "assessment_not_found", "message": "Assessment not found."}}` | The `assessment_id` does not exist or is not in the API key's org. |

## Cancel (archive) an assessment

<ParamField path="method" type="DELETE">
  `DELETE /v1/assessments/{assessment_id}`
</ParamField>

Cancels an in-flight assessment or archives a completed one. Mirrors the in-product "Archive" action on the assessment page.

The operation is a **soft delete**: the assessment row plus its evals and findings are marked `is_archived=true`, the assessment's portal (if any) is closed, and any in-flight workflow runs targeting the assessment are canceled in the workflow engine on a best-effort basis. The underlying rows are retained for audit; `GET /v1/assessments/{assessment_id}` will continue to return them.

### Path parameters

<ParamField path="assessment_id" type="string" required>
  The assessment ID (`cbqsrw_...`).
</ParamField>

### Example request

```bash cURL theme={null}
curl -X DELETE "https://api.coverbase.app/v1/assessments/cbqsrw_2d5e8f1a4b7c0d3e6f9a2b5c8d1e4f7a" \
  -H "Authorization: Bearer ak_live_xxx"
```

### Example response

`200 OK`:

```json theme={null}
{
  "id": "cbqsrw_2d5e8f1a4b7c0d3e6f9a2b5c8d1e4f7a",
  "status": "canceled",
  "is_archived": true,
  "archived_at": 1746580000
}
```

### Response fields

<ResponseField name="id" type="string">
  The archived assessment ID.
</ResponseField>

<ResponseField name="status" type="string">
  Assessment status at the time of archival. The workflow engine receives a best-effort cancel signal immediately after archive; transient runs may still surface a brief `canceled` lifecycle event.
</ResponseField>

<ResponseField name="is_archived" type="boolean">
  Always `true` after a successful archive.
</ResponseField>

<ResponseField name="archived_at" type="integer">
  Unix timestamp (seconds) when the archive took effect.
</ResponseField>

### Error responses

| Status | Body                                                                               | When                                                               |
| ------ | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| 404    | `{"detail": {"code": "assessment_not_found", "message": "Assessment not found."}}` | The `assessment_id` does not exist or is not in the API key's org. |

<Note>
  Archive is idempotent: archiving an already-archived assessment returns `200` with the same envelope. To restore an archived assessment, use the Coverbase dashboard.
</Note>
