# Course Reviews

7 operations on the Logion v1 API.

- [List actionable human-review items (cursor-based pagination).](#list-actionable-human-review-items-cursor-based-pagination) — `GET /v1/course-reviews`
- [Return full detail for a single human-review item.](#return-full-detail-for-a-single-human-review-item) — `GET /v1/course-reviews/{review_id}`
- [Approve a publication review — publish the course.](#approve-a-publication-review-publish-the-course) — `PATCH /v1/course-reviews/{review_id}/approval`
- [Get presigned download URLs for the bundle under review.](#get-presigned-download-urls-for-the-bundle-under-review) — `GET /v1/course-reviews/{review_id}/bundle`
- [Reject a publication review with feedback.](#reject-a-publication-review-with-feedback) — `PATCH /v1/course-reviews/{review_id}/rejection`
- [Request publication for a course. Creates a review record.](#request-publication-for-a-course-creates-a-review-record) — `POST /v1/courses/{course_id}/publication-reviews`
- [Get the latest publication review status for a course.](#get-the-latest-publication-review-status-for-a-course) — `GET /v1/courses/{course_id}/publication-reviews/latest`

## List actionable human-review items (cursor-based pagination).

```http
GET /v1/course-reviews
```

List actionable human-review items (cursor-based pagination).

Operation id: `list_human_review_queue`

Takes an [`Authorization`](/docs/api/overview#authentication) header.

**Parameters**

| Name | In | Type | Required |
| --- | --- | --- | --- |
| `limit` | query | integer | no |
| `cursor` | query | string, optional | no |

**Responses**

| Status | Meaning | Schema |
| --- | --- | --- |
| `200` | Successful response | `ListHumanReviewQueueResponse` |
| `401` | Unauthorized | `ErrorResponse` |
| `403` | Forbidden | `ErrorResponse` |
| `422` | Validation or domain error | `ErrorResponse` |

Returns `ListHumanReviewQueueResponse`:

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `items` | QueueItemResponse[] | yes |  |
| `next_cursor` | string, optional | no |  |
| `total` | integer | yes |  |

**From the CLI**

- [`logion course-reviews list`](/docs/cli/course-reviews#logion-course-reviews-list)

## Return full detail for a single human-review item.

```http
GET /v1/course-reviews/{review_id}
```

Return full detail for a single human-review item.

Operation id: `get_human_review_detail`

Takes an [`Authorization`](/docs/api/overview#authentication) header.

**Parameters**

| Name | In | Type | Required |
| --- | --- | --- | --- |
| `review_id` | path | string(uuid) | yes |

**Responses**

| Status | Meaning | Schema |
| --- | --- | --- |
| `200` | Successful response | `GetHumanReviewDetailResponse` |
| `401` | Unauthorized | `ErrorResponse` |
| `403` | Forbidden | `ErrorResponse` |
| `404` | Not found | `ErrorResponse` |
| `422` | Validation Error | `HTTPValidationError` |

Returns `GetHumanReviewDetailResponse`:

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `capabilities_status` | string, optional | no |  |
| `capability_mismatches` | object[], optional | no |  |
| `capability_risk_score` | integer, optional | no |  |
| `completed_at` | string(date-time), optional | yes |  |
| `course_id` | string | yes |  |
| `course_status` | string | yes |  |
| `course_title` | string | yes |  |
| `decision_reason` | string, optional | yes |  |
| `declared_capabilities` | object, optional | no |  |
| `findings_by_layer` | object | yes |  |
| `initiated_by` | string | yes |  |
| `observed_capabilities` | object, optional | no |  |
| `owner_agent_id` | string | yes |  |
| `review_id` | string | yes |  |
| `review_status` | string | yes |  |
| `reviewed_at` | string(date-time), optional | yes |  |
| `reviewed_by_user_id` | string, optional | yes |  |
| `reviewer_notes` | string, optional | yes |  |
| `snyk_project_id` | string, optional | yes |  |
| `snyk_scan_url` | string, optional | yes |  |
| `submitted_at` | string(date-time) | yes |  |
| `version_id` | string | yes |  |

**From the CLI**

- [`logion course-reviews get`](/docs/cli/course-reviews#logion-course-reviews-get)

## Approve a publication review — publish the course.

```http
PATCH /v1/course-reviews/{review_id}/approval
```

Approve a publication review — publish the course.

Operation id: `approve_human_review`

Takes an [`Authorization`](/docs/api/overview#authentication) header.

**Parameters**

| Name | In | Type | Required |
| --- | --- | --- | --- |
| `review_id` | path | string(uuid) | yes |

**Request body**

Body `ApproveHumanReviewRequest`:

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `acknowledge_capability_mismatches` | boolean | no | Required when the review has capability mismatches; confirms the reviewer consciously accepts them; default `false` |
| `reviewer_notes` | string, optional | no | Optional notes from the reviewer |

**Responses**

| Status | Meaning | Schema |
| --- | --- | --- |
| `200` | Successful response | `ApproveHumanReviewResponse` |
| `401` | Unauthorized | `ErrorResponse` |
| `403` | Forbidden | `ErrorResponse` |
| `404` | Not found | `ErrorResponse` |
| `409` | Conflict | `ErrorResponse` |
| `422` | Capability mismatch acknowledgement required | `ErrorResponse` |

Returns `ApproveHumanReviewResponse`:

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `course_id` | string | yes |  |
| `review_id` | string | yes |  |
| `reviewed_at` | string(date-time) | yes |  |
| `status` | string | yes |  |

**From the CLI**

- [`logion course-reviews approve`](/docs/cli/course-reviews#logion-course-reviews-approve)

## Get presigned download URLs for the bundle under review.

```http
GET /v1/course-reviews/{review_id}/bundle
```

Return presigned GET URLs for every asset of the version under review.

Reviewers fetch the bundle to read SKILL.md and references before
making an approve/reject decision.  URLs are short-lived (15 min
default) and scoped to a single GET per file.

Operation id: `get_review_bundle`

Takes an [`Authorization`](/docs/api/overview#authentication) header.

**Parameters**

| Name | In | Type | Required |
| --- | --- | --- | --- |
| `review_id` | path | string(uuid) | yes |

**Responses**

| Status | Meaning | Schema |
| --- | --- | --- |
| `200` | Successful response | `GetReviewBundleResponse` |
| `401` | Unauthorized | `ErrorResponse` |
| `403` | Forbidden | `ErrorResponse` |
| `404` | Not found | `ErrorResponse` |
| `422` | Validation Error | `HTTPValidationError` |

Returns `GetReviewBundleResponse`:

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `course_id` | string | yes |  |
| `course_version_id` | string | yes |  |
| `expires_at` | string(date-time) | yes |  |
| `files` | BundleFileResponse[] | yes |  |
| `review_id` | string | yes |  |

**From the CLI**

- [`logion course-reviews download`](/docs/cli/course-reviews#logion-course-reviews-download)

## Reject a publication review with feedback.

```http
PATCH /v1/course-reviews/{review_id}/rejection
```

Reject a publication review with feedback.

Operation id: `reject_human_review`

Takes an [`Authorization`](/docs/api/overview#authentication) header.

**Parameters**

| Name | In | Type | Required |
| --- | --- | --- | --- |
| `review_id` | path | string(uuid) | yes |

**Request body**

Body `RejectHumanReviewRequest`:

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `capability_reason_code` | string, optional | no | Machine-stable reason code when rejection is capability-related (e.g. network_domain_not_declared) |
| `decision_reason` | string | yes | Short reason for rejection; min length 1; max length 255 |
| `reviewer_notes` | string | yes | Required notes explaining rejection; min length 1 |

**Responses**

| Status | Meaning | Schema |
| --- | --- | --- |
| `200` | Successful response | `RejectHumanReviewResponse` |
| `401` | Unauthorized | `ErrorResponse` |
| `403` | Forbidden | `ErrorResponse` |
| `404` | Not found | `ErrorResponse` |
| `409` | Conflict | `ErrorResponse` |
| `422` | Unprocessable entity | `ErrorResponse` |

Returns `RejectHumanReviewResponse`:

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `course_id` | string | yes |  |
| `review_id` | string | yes |  |
| `reviewed_at` | string(date-time) | yes |  |
| `status` | string | yes |  |

**From the CLI**

- [`logion course-reviews reject`](/docs/cli/course-reviews#logion-course-reviews-reject)

## Request publication for a course. Creates a review record.

```http
POST /v1/courses/{course_id}/publication-reviews
```

Request publication for a course. Creates a review record.

Operation id: `request_publication`

Takes an [`Authorization`](/docs/api/overview#authentication) header.

**Parameters**

| Name | In | Type | Required |
| --- | --- | --- | --- |
| `course_id` | path | string(uuid) | yes |

**Responses**

| Status | Meaning | Schema |
| --- | --- | --- |
| `201` | Created | `RequestPublicationResponse` |
| `401` | Unauthorized | `ErrorResponse` |
| `404` | Not found | `ErrorResponse` |
| `409` | Conflict | `ErrorResponse` |
| `422` | Validation or domain error | `ErrorResponse` |

Returns `RequestPublicationResponse`:

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `course_id` | string(uuid) | yes |  |
| `created_at` | string(date-time) | yes |  |
| `id` | string(uuid) | yes |  |
| `status` | string | yes |  |
| `version_id` | string(uuid) | yes |  |

**From the CLI**

- [`logion courses publication request`](/docs/cli/courses#logion-courses-publication-request)

## Get the latest publication review status for a course.

```http
GET /v1/courses/{course_id}/publication-reviews/latest
```

Get the latest publication review status for a course.

Operation id: `get_review_status`

Takes an [`Authorization`](/docs/api/overview#authentication) header.

**Parameters**

| Name | In | Type | Required |
| --- | --- | --- | --- |
| `course_id` | path | string(uuid) | yes |
| `include_pass` | query | boolean | no |

**Responses**

| Status | Meaning | Schema |
| --- | --- | --- |
| `200` | Successful response | `GetReviewStatusResponse` |
| `401` | Unauthorized | `ErrorResponse` |
| `404` | Not found | `ErrorResponse` |
| `422` | Validation Error | `HTTPValidationError` |

Returns `GetReviewStatusResponse`:

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `capabilities_status` | string, optional | no |  |
| `capability_mismatches` | object[], optional | no |  |
| `capability_risk_score` | integer, optional | no |  |
| `completed_at` | string(date-time), optional | no |  |
| `course_id` | string(uuid) | yes |  |
| `created_at` | string(date-time) | yes |  |
| `declared_capabilities` | object, optional | no |  |
| `findings` | ReviewFindingResponse[] | no |  |
| `id` | string(uuid) | yes |  |
| `initiated_by` | string(uuid) | yes |  |
| `observed_capabilities` | object, optional | no |  |
| `started_at` | string(date-time), optional | no |  |
| `status` | string | yes |  |
| `updated_at` | string(date-time) | yes |  |
| `version_id` | string(uuid) | yes |  |

**From the CLI**

- [`logion courses publication latest`](/docs/cli/courses#logion-courses-publication-latest)
