---
title: "Update campaign item reviewers"
method: POST
path: "/campaigns/{campaign_id}/item-reviewers"
tags: ["campaigns"]
---

# Update campaign item reviewers

`POST /campaigns/{campaign_id}/item-reviewers`

## Request

- Base URL: `https://api.opal.dev/v1`
- URL: `https://api.opal.dev/v1/campaigns/{campaign_id}/item-reviewers`
- Auth: HTTP bearer

Atomically assigns and unassigns reviewers on campaign items in one
request. All `campaign_item_id`s must belong to `{campaign_id}`.
Existing assign pairs are ignored; missing unassign pairs are skipped.
Completed reviews (`decided_at` set) are never removed.
Requires settings-edit permission. Returns 400 if the campaign has ended.

## Path parameters

- `campaign_id` string, uuid, required

## Request body

- UpdateCampaignItemReviewersInfo: Input for updating campaign item reviewers.
  - `to_assign` CampaignItemReviewerAssignment[]: Pairs to assign. Existing pairs are ignored.
    - `campaign_item_id` string, uuid, required: The ID of the campaign item.
    - `reviewer_id` string, uuid, nullable: The ID of the reviewer. Required when `assignment_source` is `MANUAL` (or omitted), and required for every unassign entry.
    - `assignment_source` 'MANUAL' | 'PRINCIPAL_MANAGER' | 'ASSET_ADMIN_OWNER' | 'PRINCIPAL_SELF': How a reviewer was assigned to a campaign item.
  - `to_unassign` CampaignItemReviewerAssignment[]: Pairs to unassign. Only pending `MANUAL` pairs are removed; missing pairs are skipped. `reviewer_id` is required on each entry.
    - `campaign_item_id` string, uuid, required: The ID of the campaign item.
    - `reviewer_id` string, uuid, nullable: The ID of the reviewer. Required when `assignment_source` is `MANUAL` (or omitted), and required for every unassign entry.
    - `assignment_source` 'MANUAL' | 'PRINCIPAL_MANAGER' | 'ASSET_ADMIN_OWNER' | 'PRINCIPAL_SELF': How a reviewer was assigned to a campaign item.
  - `to_remove_sources` CampaignItemReviewerSourceRemoval[]: Remove all pending rows for the given assignment sources.
    - `campaign_item_id` string, uuid, required: The ID of the campaign item.
    - `assignment_source` 'MANUAL' | 'PRINCIPAL_MANAGER' | 'ASSET_ADMIN_OWNER' | 'PRINCIPAL_SELF', required: How a reviewer was assigned to a campaign item.

## Response `200`

Reviews for all referenced items after the update.

- UpdateCampaignItemReviewersResult: Result of updating campaign item reviewers.
  - `reviews` CampaignItemReview[], required: Full review set for every referenced item after the update.
    - `campaign_item_review_id` string, uuid, required: The ID of the campaign item review.
    - `campaign_item_id` string, uuid, required: The ID of the campaign item.
    - `reviewer_user_id` string, uuid, required: The ID of the assigned reviewer.
    - `assignment_source` 'MANUAL' | 'PRINCIPAL_MANAGER' | 'ASSET_ADMIN_OWNER' | 'PRINCIPAL_SELF', required: How a reviewer was assigned to a campaign item.
    - `decision` 'APPROVED' | 'REVOKED' | 'CHANGE_ROLE' | 'REDUCE_EXPIRATION' | 'ADMIN_REVOKED' | 'NO_ACTION' | 'REASSIGNED' | 'ADMIN_OVERRIDE_APPROVED' | 'ADMIN_OVERRIDE_REVOKED': Decision recorded on a campaign item review row.
    - `note` string, nullable: Optional note from the reviewer.
    - `updated_access_level_remote_id` string, nullable: Target access level remote ID when decision is CHANGE_ROLE.
    - `updated_duration_in_minutes` integer, nullable: Built-in duration in minutes when decision is REDUCE_EXPIRATION.
    - `decided_at` string, date-time, nullable: When the decision was finalized.
    - `resolution_message` string, nullable: Preset assignment summary when a manager/owner chain was resolved or substituted.
  - `unassigned_preset_messages` CampaignItemUnassignedPresetMessage[], required: Preset assignment messages for items that did not produce a review row.
    - `campaign_item_id` string, uuid, required: The ID of the campaign item.
    - `assignment_source` 'MANUAL' | 'PRINCIPAL_MANAGER' | 'ASSET_ADMIN_OWNER' | 'PRINCIPAL_SELF', required: How a reviewer was assigned to a campaign item.
    - `message` string, required: Human-readable explanation of why no reviewer was assigned.

## Other responses

- `400`: Invalid input, items not in this campaign, campaign ended, or self-review violation.
- `403`: Missing settings-edit permission.
- `404`: Campaign or one or more campaign items not found.

## Changes

- **2026-10-06** `c0ce333f6196`: 1 warning, 1 info
  - added the new `REDUCE_EXPIRATION` enum value to the `reviews/items/decision/allOf[#/components/schemas/CampaignItemReviewDecisionEnum]/` response property for the response status `200`
  - added the optional property `reviews/items/updated_duration_in_minutes` to the response with the `200` status
- **2026-09-24** `9ac628dcda14`: 2 warning, 3 info
  - added the new `PRINCIPAL_SELF` enum value to the `reviews/items/assignment_source` response property for the response status `200`
  - added the new `PRINCIPAL_SELF` enum value to the `unassigned_preset_messages/items/assignment_source` response property for the response status `200`
  - added the new `PRINCIPAL_SELF` enum value to the request property `to_assign/items/assignment_source/allOf[#/components/schemas/CampaignItemReviewerAssignmentSourceEnum]/`
  - added the new `PRINCIPAL_SELF` enum value to the request property `to_remove_sources/items/assignment_source`
  - …1 more
- **2026-09-15** `afb90e6ded01`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/opal/apis/opal-api/changes/campaigns/:campaign_id/item-reviewers/post.md)

---

[API](https://skmtc.dev/opal/apis/opal-api.md) - [All operations](https://skmtc.dev/opal/apis/opal-api/llms.txt) - [OpenAPI document](https://skmtc.dev/opal/apis/opal-api/revisions/e7877ea96eab?raw)
