> ## 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.

# Control sets API

> Create and configure control sets, their sections and controls, including AI guidance, strictness, evidence sources and Zero Touch eligibility.

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.

These endpoints let you build and tune the control sets your vendors are assessed against. A **control set** (`cbctst_...`) holds **sections** (`cbctsn_...`) and **controls** (`cbctrl_...`). Settings on the set apply to every control in it: AI evaluation guidance, strictness, evidence sources, applicability and Zero Touch eligibility.

Controls are versioned. A material edit to a control creates a new version with a new `id` and the same `perma_id`, so past assessments keep the text they were evaluated on. Control sets and sections are edited in place. Every change to a set or control is recorded in its activity, as the same edit in the dashboard is.

All endpoints are org-scoped to the API key. See [API conventions](/conventions.md) for shared behavior. None of these honor `Idempotency-Key`.

| Method | Path | Permission |
| - | - | - |
| `GET` | `/v1/control-sets` | `control_set:read:org` |
| `POST` | `/v1/control-sets` | `control_set:create:org` |
| `GET` | `/v1/control-sets/{control_set_id}` | `control_set:read:org` |
| `PATCH` | `/v1/control-sets/{control_set_id}` | `control_set:update:org` |
| `PATCH` | `/v1/control-sets/bulk` | `control_set:update:org` |
| `GET` | `/v1/control-sets/{control_set_id}/stats` | `control_set:read:org` |
| `GET` | `/v1/control-sets/{control_set_id}/sections` | `control_set:read:org` |
| `POST` | `/v1/control-sets/{control_set_id}/sections` | `control_set:update:org` |
| `PATCH` | `/v1/control-sections/{control_section_id}` | `control_set:update:org` |
| `GET` | `/v1/control-sets/{control_set_id}/controls` | `control:read:org` |
| `POST` | `/v1/control-sets/{control_set_id}/controls` | `control:create:org` |
| `GET` | `/v1/controls/{control_id}` | `control:read:org` |
| `PATCH` | `/v1/controls/{control_id}` | `control:update:org` |
| `PATCH` | `/v1/controls/bulk` | `control:update:org` |
| `GET` | `/v1/controls/{control_id}/history` | `control:read:org` |
| `POST` | `/v1/controls/bulk-action` | `control:update:org` |
| `PATCH` | `/v1/controls/{control_id}/framework-references/{index}` | `control:update:org` |
| `GET` | `/v1/control-set-library` | `control_set:read:org` |
| `GET` | `/v1/control-set-library/updates` | `control_set:read:org` |
| `POST` | `/v1/control-sets/{control_set_id}/apply-library-update` | `control_set:update:org` |
| `POST` | `/v1/control-sets/{control_set_id}/reassess-affected` | `control_set:update:org` |
| `POST` | `/v1/control-sets/{control_set_id}/crosswalk` | `control_set:update:org` |
| `GET` | `/v1/control-sets/{control_set_id}/crosswalk` | `control_set:read:org` |
| `POST` | `/v1/control-sets/{control_set_id}/exports` | `control_set:export:org` |
| `GET` | `/v1/control-set-exports/{export_id}` | `control_set:export:org` |
| `GET` | `/v1/vendors/{vendor_id}/control-set-matches` | `control_set:read:org` |
| `GET` | `/v1/assessments/{assessment_id}/control-set-matches` | `control_set:read:org` |

Archiving or unarchiving a set or section also needs `control_set:archive:org`, and archiving a control needs `control:archive:org`. Archiving and unarchiving stay with signed-in users: in every organization, a request made with an API key that archives or unarchives returns `403 api_key_not_allowed`.

## Control set object

**id** `string`

Control set ID (`cbctst_...`).

**name** `string`

Name.

**control\_set\_type** `string`

`policy`, `questionnaire` or `internal`.

**description** `string | null`

Description.

**is\_archived** `boolean`

Whether the set is archived.

**is\_applied\_to\_all** `boolean`

Whether the set applies to every vendor.

**applied\_to\_tag\_ids** `string[]`

Tags whose vendors the set applies to.

**inherent\_risk\_level\_ids** `string[]`

Inherent risk levels whose vendors the set applies to.

**is\_zero\_touch\_eligible** `boolean`

Whether Zero Touch assessments can use the set.

**eval\_strictness** `integer`

How strictly the AI judges evidence, from `1` (lenient) to `5` (skeptical). New sets use `3`.

**eval\_instructions** `string | null`

Guidance the AI applies when it evaluates every control in the set.

**remediation\_instructions** `string | null`

Guidance for remediation wording.

**is\_web\_search\_enabled** `boolean`

Whether evaluations search the web.

**web\_search\_instructions** `string | null`

Guidance for web search.

**evidence\_sources** `object[]`

The [evidence sources](#evidence-source-object) the AI draws on.

**restrict\_evidence\_to\_sources** `boolean`

Whether the AI uses only the listed evidence sources.

**document\_requirement** `object[]`

Documents vendors provide, each `{ "document_type": "<slug>", "is_required": true }`.

**risk\_domain\_id** `string | null`

Risk domain the set rolls up to.

**vendor\_rating\_weight** `number | null`

The set's share of the vendor rating. `null` leaves it out of the weighting.

**scale\_id** `string | null`

The scale every live control uses. `null` when the controls use different scales or none.

**watcher\_ids** `string[]`

Users who watch the set.

**library\_slug** `string | null`

The library template the set was copied from.

**library\_revision** `integer | null`

The template revision the copy carries.

**questionnaire\_id** `string | null`

The questionnaire derived from the set.

**control\_count** `integer`

Number of live controls.

**created\_at** `integer`

Unix timestamp (seconds).

### Evidence source object

**source\_type** `string`

`document`, `factsheet`, `web_search` or `contracts`.

**document\_types** `string[]`

For `document`: the document types to search. Empty means every type.

**instructions** `string | null`

How the AI uses the source.

**is\_required** `boolean`

When `true`, the AI cites this source or flags its absence.

**match** `string`

For a required `document` source: `any` (one type is enough) or `all`.

**minimum\_credibility** `string`

For `web_search`: the lowest publisher credibility accepted.

**denied\_web\_evidence\_types** `string[]`

For `web_search`: page types to exclude.

A `contracts` source shares contract terms with every evaluation, so adding one needs `contract:read:org`.

## List control sets

**method** `GET`

`GET /v1/control-sets`

### Query parameters

**limit** `integer` (default: 50)

Page size, from 1 to 200.

**offset** `integer` (default: 0)

Number of sets to skip.

**include\_archived** `boolean` (default: false)

Include archived sets.

Returns an `items` array of [control set objects](#control-set-object), newest first, with `total`, `limit` and `offset`.

## Create a control set

**method** `POST`

`POST /v1/control-sets`

Returns `201 Created` with the [control set object](#control-set-object). To copy a framework template with its sections and controls, send its slug from [the library](#list-library-templates) as `use_library`.

### Request body

**name** `string` *required*

Name.

**control\_set\_type** `string` (default: policy)

`policy`, `questionnaire` or `internal`. A library copy takes the template's type.

**description** `string`

Description.

**is\_applied\_to\_all** `boolean` (default: false)

Apply the set to every vendor.

**applied\_to\_tag\_ids** `string[]`

Tags whose vendors the set applies to.

**is\_zero\_touch\_eligible** `boolean`

Make the set available to Zero Touch assessments.

**eval\_strictness** `integer`

From `1` (lenient) to `5` (skeptical).

**eval\_instructions** `string`

Set-level AI guidance.

**remediation\_instructions** `string`

Remediation guidance. A library copy ignores it.

**is\_web\_search\_enabled** `boolean` (default: false)

Let evaluations search the web.

**web\_search\_instructions** `string`

Web search guidance, at most 2000 characters.

**evidence\_sources** `object[]`

[Evidence sources](#evidence-source-object).

**restrict\_evidence\_to\_sources** `boolean` (default: true)

Use only the listed evidence sources.

**document\_requirement** `object[]`

Required documents. A library copy ignores it.

**risk\_domain\_id** `string`

Risk domain. A library copy otherwise takes the template's domain.

**scale\_id** `string`

Scale for every control of the set.

**watcher\_ids** `string[]`

Users who watch the set.

**use\_library** `string`

Library template slug to copy.

### Example request

```bash cURL theme={null}
curl -X POST "https://api.coverbase.app/v1/control-sets" \
  -H "Authorization: Bearer ak_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "SOC 2 for critical vendors",
    "use_library": "soc2",
    "is_zero_touch_eligible": true,
    "eval_strictness": 4
  }'
```

## Retrieve a control set

**method** `GET`

`GET /v1/control-sets/{control_set_id}`

Returns the [control set object](#control-set-object), archived or live, or `404 control_set_not_found`.

## Update a control set

**method** `PATCH`

`PATCH /v1/control-sets/{control_set_id}`

Only the fields you send change. To clear `risk_domain_id` or `vendor_rating_weight`, send `null`. An archived set returns `409 control_set_archived`. Returns the updated [control set object](#control-set-object).

### Request body

Every create field except `control_set_type` and `use_library`, plus:

**inherent\_risk\_level\_ids** `string[]`

Inherent risk levels the set applies to. Replaces the list.

**vendor\_rating\_weight** `number`

Share of the vendor rating, greater than 0 and at most 1000.

**is\_archived** `boolean`

Archive the set with its sections and controls. Needs `control_set:archive:org`. An API key gets `403 api_key_not_allowed`.

Lists you send (`applied_to_tag_ids`, `evidence_sources`, `document_requirement`, `watcher_ids`) replace the stored list. A `scale_id` applies to every live control.

## Update many control sets

**method** `PATCH`

`PATCH /v1/control-sets/bulk`

Applies up to 200 updates in one call. If any update fails, none is applied, and the error names the failing item in `detail.id`. Each control set may appear once per call.

**updates** `object[]` *required*

From 1 to 200 items. Each item takes `control_set_id` and the fields of [Update a control set](#update-a-control-set), with the same rules.

Returns an `items` array of the updated [control set objects](#control-set-object), in request order.

## Get control set stats

**method** `GET`

`GET /v1/control-sets/{control_set_id}/stats`

Returns `vendor_count` (vendors the live set applies to) and `vendor_percentage` (their share of the org's vendors).

## List sections

**method** `GET`

`GET /v1/control-sets/{control_set_id}/sections`

**limit** `integer` (default: 100)

Page size, from 1 to 500.

**offset** `integer` (default: 0)

Number of sections to skip.

**include\_archived** `boolean` (default: false)

Include archived sections.

Returns an `items` array of [section objects](#section-object) in display order, with `total`, `limit` and `offset`.

### Section object

**id** `string`

Section ID (`cbctsn_...`).

**control\_set\_id** `string`

Parent control set.

**name** `string`

Name.

**description** `string | null`

Description.

**external\_idx** `string`

Section reference, for example `CC6`.

**sort\_order** `string`

Fractional order key.

**is\_archived** `boolean`

Whether the section is archived.

**websearch\_enabled\_override** `boolean | null`

Overrides the set's web search setting. `null` inherits it.

**web\_search\_instructions** `string | null`

Web search guidance.

**risk\_domain\_id** `string | null`

Risk domain.

**watcher\_ids** `string[]`

Users who watch the section.

**created\_at** `integer`

Unix timestamp (seconds).

## Create a section

**method** `POST`

`POST /v1/control-sets/{control_set_id}/sections`

Returns `201 Created` with the [section object](#section-object). An archived set returns `409 control_set_archived`. Every watcher must be a member of your organization, or the request returns `422 invalid_watcher`.

**name** `string` *required*

Name.

**external\_idx** `string` *required*

Section reference.

**description** `string`

Description.

**sort\_order** `string`

Fractional order key. Without it, the section goes last.

**websearch\_enabled\_override** `boolean`

Override the set's web search setting.

**web\_search\_instructions** `string`

Web search guidance, at most 2000 characters.

**risk\_domain\_id** `string`

Risk domain.

**watcher\_ids** `string[]`

Users who watch the section.

## Update a section

**method** `PATCH`

`PATCH /v1/control-sections/{control_section_id}`

Takes the create fields and `is_archived`. Only the fields you send change. To clear `risk_domain_id`, send `null`. Archiving a section archives its controls and needs `control_set:archive:org`. An API key that archives or unarchives a section gets `403 api_key_not_allowed`. An archived section accepts only `is_archived: false`. Returns the updated [section object](#section-object).

## Control object

**id** `string`

Control version ID (`cbctrl_...`).

**perma\_id** `string`

ID shared by every version of the control.

**version** `integer`

Version number, starting at 1.

**control\_set\_id** `string`

Control set.

**control\_section\_id** `string | null`

Section.

**external\_idx** `string | null`

Reference, for example `SOC2-CC6.1`. Unique within the set.

**expectation** `string`

What the vendor must do.

**question** `string | null`

The question vendors answer.

**question\_additional\_instructions** `string | null`

Extra guidance shown with the question.

**response\_type** `string`

How the question is answered, for example `free_text`, `select_one` or `select_multiple`.

**eval\_instructions** `string | null`

Guidance the AI follows when it evaluates the control.

**weight** `integer | null`

Scoring weight.

**is\_required** `boolean`

Whether an answer is required.

**allow\_comments** `boolean`

Whether respondents can add a comment.

**require\_comment** `boolean`

Whether a comment is required.

**supporting\_document\_types** `string[]`

Supporting document type slugs.

**websearch\_enabled\_override** `boolean | null`

Overrides the set's web search setting.

**web\_search\_instructions** `string | null`

Web search guidance.

**evidence\_sources** `object[]`

[Evidence sources](#evidence-source-object) for this control. Empty means the set's sources apply.

**evidence\_requirement** `string | null`

`document_required` or `attestation_only`.

**significant\_deficiency\_level\_id** `string | null`

A risk scale level. When set, a failing control floors the score at this level.

**significant\_deficiency\_scope** `string | null`

What the floor applies to, for example `risk_domain`.

**scale\_id** `string | null`

Scale the control is scored on.

**is\_archived** `boolean`

Whether this version is archived. An earlier version is archived once a newer one exists.

**options** `object[]`

Live answer choices, each with `id`, `perma_id`, `name`, `description`, `value` and `sort_order`.

**created\_at** `integer`

Unix timestamp (seconds).

## List controls

**method** `GET`

`GET /v1/control-sets/{control_set_id}/controls`

**limit** `integer` (default: 100)

Page size, from 1 to 500.

**offset** `integer` (default: 0)

Number of controls to skip.

**include\_archived** `boolean` (default: false)

Include archived controls.

Returns the latest version of each control as an `items` array of [control objects](#control-object), in set order, with `total`, `limit` and `offset`.

## Create a control

**method** `POST`

`POST /v1/control-sets/{control_set_id}/controls`

Returns `201 Created` with version 1 of the [control object](#control-object). An archived set returns `409 control_set_archived`. To set `evidence_requirement` or the significant-deficiency floor, send a `PATCH` afterwards.

**expectation** `string` *required*

What the vendor must do.

**control\_section\_id** `string`

A live section of the same set.

**external\_idx** `string`

Reference. A duplicate within the set returns `409 duplicate_external_idx`.

**question** `string`

The question vendors answer.

**question\_additional\_instructions** `string`

Extra guidance shown with the question.

**response\_type** `string` (default: free\_text)

How the question is answered.

**options** `object[]`

Answer choices, each `{ "name": "Yes", "description": null, "value": 1, "sort_order": null }`.

**eval\_instructions** `string`

AI evaluation guidance.

**weight** `integer` (default: 1)

Scoring weight.

**is\_required** `boolean` (default: true)

Whether an answer is required.

**allow\_comments** `boolean`

Let respondents add a comment.

**require\_comment** `boolean`

Require a comment. Turns on `allow_comments`.

**supporting\_document\_types** `string[]`

Supporting document type slugs.

**websearch\_enabled\_override** `boolean`

Override the set's web search setting.

**web\_search\_instructions** `string`

Web search guidance, at most 2000 characters.

**evidence\_sources** `object[]`

[Evidence sources](#evidence-source-object).

**scale\_id** `string`

Scale. Without it, the org's default scale applies.

## Retrieve a control

**method** `GET`

`GET /v1/controls/{control_id}`

Returns the [control object](#control-object) for that version, archived or live, or `404 control_not_found`.

## Update a control

**method** `PATCH`

`PATCH /v1/controls/{control_id}`

Takes every create field, all optional, plus the fields below. Only the fields you send change. Returns the resulting [control object](#control-object).

A material change creates a new version, and the response carries its new `id`. These fields are material: `expectation`, `question`, `question_additional_instructions`, `response_type`, `eval_instructions`, `weight`, `is_required`, `control_section_id`, `significant_deficiency_level_id`, `significant_deficiency_scope` and `options`. Other fields change the version in place.

**evidence\_requirement** `string`

`document_required` or `attestation_only`. Send `null` to clear it.

**significant\_deficiency\_level\_id** `string`

A risk scale level. Send `null` to clear the floor.

**significant\_deficiency\_scope** `string`

What the floor applies to. Send `null` to clear it.

**control\_section\_id** `string`

Move to this section of the same set. Send `unassigned` to remove it from its section.

**options** `object[]`

Answer choices to add or edit. A new choice has no `id`. To edit an existing choice, send its `id` and `perma_id` with the fields to change, or `"is_archived": true` to remove it.

**is\_archived** `boolean`

Archive or unarchive the control. Needs `control:archive:org`. An API key gets `403 api_key_not_allowed`.

If you edit an earlier version, the request returns `409 control_superseded` with the latest version's ID in the message.

### Example request

```bash cURL theme={null}
curl -X PATCH "https://api.coverbase.app/v1/controls/cbctrl_1234" \
  -H "Authorization: Bearer ak_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "eval_instructions": "Accept a SOC 2 bridge letter dated within 3 months.",
    "evidence_requirement": null
  }'
```

## Update many controls

**method** `PATCH`

`PATCH /v1/controls/bulk`

Applies up to 200 control updates in one call. If any update fails, none is applied, and the error names the failing item in `detail.id`. Each control may appear once per call.

**updates** `object[]` *required*

From 1 to 200 items. Each item takes `control_id` and the fields of [Update a control](#update-a-control), with the same rules.

Returns an `items` array in request order. Each item has `previous_id` (the ID you sent) and `control` (the resulting [control object](#control-object)). A material change gives `control` a new `id`.

## Get control history

**method** `GET`

`GET /v1/controls/{control_id}/history`

**limit** `integer` (default: 20)

Versions per page, from 1 to 100.

**offset** `integer` (default: 0)

Page number, counted in pages of `limit` versions.

Returns an `items` array of [control objects](#control-object), newest version first, starting from the version you name.

## Move, archive or unarchive controls

**method** `POST`

`POST /v1/controls/bulk-action`

Moves many controls into one section, or archives or unarchives them. If any control fails, nothing changes. The section must belong to the same control set as every control, or the request returns `422 invalid_bulk_action`. Unarchiving an earlier version of a control returns `409 control_superseded`, because a newer version is live. Each moved control takes a new version. Returns `{ "action": "add_to_section", "control_count": 2 }`.

**action** `string` *required*

`add_to_section`, `archive` or `unarchive`. Archiving and unarchiving needs `control:archive:org`, and an API key gets `403 api_key_not_allowed`.

**control\_ids** `string[]` *required*

From 1 to 500 control IDs.

**control\_section\_id** `string`

Destination section. Required for `add_to_section`.

## Review a framework reference

**method** `PATCH`

`PATCH /v1/controls/{control_id}/framework-references/{index}`

Accepts or rejects a suggested framework reference on a control. Only an accepted reference counts toward framework coverage.

**action** `string` *required*

`accept`, `reject` (removes the reference) or `no_clause` (records that the framework has no matching clause).

Returns `framework_references`, the control's references after the change, or `404 framework_reference_not_found`.

## Start a crosswalk

**method** `POST`

`POST /v1/control-sets/{control_set_id}/crosswalk`

Returns `202 Accepted`. Suggests, in the background, a reference in each target framework for every control of the set. A suggestion counts toward coverage only once you [accept it](#review-a-framework-reference).

**target\_control\_set\_ids** `string[]` *required*

From 1 to 50 framework control sets to map onto. Each must be a live control set of your organization other than this one, or the request returns `422 invalid_crosswalk_targets` and nothing is queued.

Returns `queued` and `target_control_set_ids`, the distinct targets that were queued. An archived subject set returns `409 control_set_archived`.

## Read a crosswalk

**method** `GET`

`GET /v1/control-sets/{control_set_id}/crosswalk`

Returns `frameworks` and `rows`. Each framework has `framework`, `control_set_id`, `accepted_clauses`, `total_clauses` and `suggested`. Each row has `control_id`, `external_idx`, `expectation`, `section` and `refs`. Each reference has `index` (the value a review takes), `framework`, `reference`, `rationale` and `review_state` (`suggested` or `accepted`).

## List library templates

**method** `GET`

`GET /v1/control-set-library`

Returns an `items` array of templates you can copy with `use_library`. Each has `slug`, `name`, `num_controls`, `control_set_type`, `audience` (`vendor` or `internal`), `description` and `is_web_search_enabled`.

## List library updates

**method** `GET`

`GET /v1/control-set-library/updates`

Returns an `items` array of the org's library copies whose template has a newer revision. Each has `control_set_id`, `control_set_name`, `template_slug`, `template_name`, `from_revision`, `to_revision`, `vendor_count` and `changes`. Each change has `external_idx`, `kind` (`added`, `changed` or `removed`), `expectation`, `previous_expectation` and `is_conflict` (the copy was edited by hand).

## Apply a library update

**method** `POST`

`POST /v1/control-sets/{control_set_id}/apply-library-update`

Brings a library copy to its template's latest revision. Changed controls take a new version. Controls edited by hand stay as they are. Returns `applied` and `conflicts` (lists of `external_idx`) and `to_revision`, or `409 no_library_update`.

## Draft a reassessment of affected vendors

**method** `POST`

`POST /v1/control-sets/{control_set_id}/reassess-affected`

Returns `201 Created` with `reassessment_id` and `vendor_count`. Drafts one [reassessment](/api-reference/reassessments.md) of the vendors assessed with the set.

**vendor\_ids** `string[]`

Vendors to include. Without it, every vendor assessed with the set is included.

## Export a control set

**method** `POST`

`POST /v1/control-sets/{control_set_id}/exports`

Returns `202 Accepted` with the export's `id` and `status`. Coverbase builds an Excel workbook of the set, named `export_filename`, in the background. Poll `GET /v1/control-set-exports/{export_id}`: once `status` is `complete`, the response carries a `download_url` that expires after `expires_in` seconds. The poll response is never cached.

## Find matching control sets

**method** `GET`

`GET /v1/vendors/{vendor_id}/control-set-matches`

Lists the control sets that apply to a vendor, one row per set, with the `reason`: applied to every vendor, the vendor's inherent risk, or one of its tags. The route also checks that you can read the vendor.

`GET /v1/assessments/{assessment_id}/control-set-matches` lists the control sets the assessment's custom questionnaires match that are not on the assessment yet. Each row has the `questionnaire_name`, a `score`, and how many questions overlap. The route also checks that you can read the assessment.

## Error responses

| Status | Body | When |
| - | - | - |
| 403 | `{"detail": {"code": "api_key_not_allowed", ...}}` | An API key archives or unarchives a set, section or control. |
| 403 | `{"detail": "Missing required permission."}` | The caller lacks the permission, including `contract:read:org` for a `contracts` evidence source. |
| 404 | `{"detail": {"code": "control_set_not_found", ...}}` | The set does not exist in the API key's org. |
| 404 | `{"detail": {"code": "control_section_not_found", ...}}` | The section does not exist. |
| 404 | `{"detail": {"code": "control_not_found", ...}}` | The control does not exist. |
| 404 | `{"detail": {"code": "export_not_found", ...}}` | The export does not exist. |
| 404 | `{"detail": {"code": "vendor_not_found", ...}}` | The vendor does not exist. |
| 404 | `{"detail": {"code": "assessment_not_found", ...}}` | The assessment does not exist. |
| 409 | `{"detail": {"code": "control_set_archived", ...}}` | Editing an archived set, or adding a section or control to one. |
| 409 | `{"detail": {"code": "duplicate_external_idx", ...}}` | The set already has a control with that `external_idx`. |
| 409 | `{"detail": {"code": "control_superseded", ...}}` | Editing or unarchiving an earlier version of a control. |
| 409 | `{"detail": {"code": "no_library_update", ...}}` | The set has no newer template revision. |
| 422 | `{"detail": {"code": "invalid_control_set", ...}}` | The set was rejected, for example an unknown library slug or risk domain. |
| 422 | `{"detail": {"code": "invalid_control_section", ...}}` | The section is not a live section of the same set. |
| 422 | `{"detail": {"code": "invalid_control_option", ...}}` | An answer choice has no name, or an edited choice has no `perma_id`. |
| 422 | `{"detail": {"code": "invalid_control_update", ...}}` | The control cannot take the change, for example an archived version. |
| 422 | `{"detail": {"code": "no_assessed_vendors", ...}}` | None of the vendors were assessed with the set. |
| 422 | `{"detail": {"code": "invalid_watcher", ...}}` | A section watcher is not a member of your organization. |
| 422 | `{"detail": {"code": "invalid_bulk_action", ...}}` | A bulk move names a missing section or one in another control set. |
| 422 | `{"detail": {"code": "invalid_crosswalk_targets", ...}}` | A crosswalk target is missing, archived, in another organization, or the set itself. |
| 422 | `{"detail": {"code": "duplicate_bulk_item", ...}}` | A bulk update names the same ID twice. |

## Related

* [Reassessments API](/api-reference/reassessments.md): run the drafts that `reassess-affected` creates.
* [Zero Touch assessments API](/api-reference/zero-touch-assessments.md): assessments that use Zero Touch eligible sets.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.