Access reviews
Record periodic reviews of who has access, with a decision per item. Records only for now.
What works today
- Reviews, items and schedules are stored in your organization.
- Each decision records who made it (
reviewed_by_user_id) and when (reviewed_at). - Nobody can decide an item about themselves.
Use it as a log of a review you run yourself. To find what to review, list policy attachments, group members and service users and their keys.
Permissions
| Action | What it allows |
|---|---|
iam:review:read | Read reviews, items and schedules. |
iam:review:write | Create reviews, items and schedules, change a review's status and delete schedules. |
iam:review:manage | Decide items. |
Reviews
| Field | Type | Description |
|---|---|---|
review_type | string | USER_ACCESS, ROLE_MEMBERS, WORKSPACE or FULL_ORG. |
scope_type, scope_id | string | Optional. What the review covers, for example a workspace. Stored only. |
reviewer_user_id | string | The person expected to do the review. Stored only. |
due_date | string | Timestamp the review is due. |
The response adds id (ar_...), organization_id, status (starts as PENDING), completed_at, created_at and updated_at.
curl -X POST https://api.hexelstudio.com/v1/reviews \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"review_type": "USER_ACCESS",
"reviewer_user_id": "<user-id>",
"due_date": "2026-11-30T00:00:00Z"
}'{
"id": "ar_...",
"organization_id": "<org-id>",
"review_type": "USER_ACCESS",
"status": "PENDING",
"reviewer_user_id": "<user-id>",
"due_date": "2026-11-30T00:00:00Z",
"created_at": "2026-10-20T09:00:00Z",
"updated_at": "2026-10-20T09:00:00Z"
}| Call | Permission | Response |
|---|---|---|
POST /v1/reviews | iam:review:write | 201 with the review |
GET /v1/reviews | iam:review:read | Array of your organization's reviews, earliest due first. Filter with ?status=. |
GET /v1/reviews/{review_id} | iam:review:read | The review, or 404 |
PATCH /v1/reviews/{review_id}/status | iam:review:write | {"status": "..."} |
Change a review's status
Send {"status": "..."} with PENDING, IN_PROGRESS, COMPLETED or CANCELLED. Any status can follow any other. COMPLETED also sets completed_at. Another value answers 400.
curl -X PATCH https://api.hexelstudio.com/v1/reviews/<review-id>/status \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"status": "COMPLETED"}'Items
An item is one piece of access to review, such as "this member has this policy".
| Field | Type | Description |
|---|---|---|
subject_type | string | Who has the access, for example USER or SERVICE_USER. |
subject_id | string | ID of that member or service user. They cannot decide this item. |
access_type | string | Kind of access, for example POLICY or GROUP. |
access_value | string | The access itself, for example a policy or group ID. |
These fields are stored as given and not checked against your organization.
curl -X POST https://api.hexelstudio.com/v1/reviews/<review-id>/items \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"subject_type": "USER",
"subject_id": "<user-id>",
"access_type": "POLICY",
"access_value": "<policy-id>"
}'The response is 201 Created with the item: id (ari_...), review_id, the four fields above, current_status (PENDING) and created_at.
| Call | Permission | Response |
|---|---|---|
POST /v1/reviews/{review_id}/items | iam:review:write | 201 with the item |
GET /v1/reviews/{review_id}/items | iam:review:read | Array of the review's items |
PATCH /v1/reviews/{review_id}/items/{item_id} | iam:review:manage | The decided item |
Decide an item
| Field | Type | Description |
|---|---|---|
decision | string | Required. APPROVE, REVOKE or FLAG. |
comment | string | Optional. |
curl -X PATCH https://api.hexelstudio.com/v1/reviews/<review-id>/items/<item-id> \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"decision": "REVOKE", "comment": "No longer on the team"}'{
"id": "ari_...",
"review_id": "<review-id>",
"subject_type": "USER",
"subject_id": "<user-id>",
"access_type": "POLICY",
"access_value": "<policy-id>",
"current_status": "REVOKED",
"reviewer_decision": "REVOKE",
"reviewer_comment": "No longer on the team",
"reviewed_by_user_id": "<your-user-id>",
"reviewed_at": "2026-10-20T09:10:00Z",
"created_at": "2026-10-20T09:00:00Z",
"decision": "REVOKE"
}current_status becomes APPROVED, REVOKED or FLAGGED. decision repeats reviewer_decision. A later decision replaces an earlier one.
| Status | When |
|---|---|
400 | decision is not APPROVE, REVOKE or FLAG. |
403 | You are the item's subject (the subject of a review item cannot decide it), or you lack iam:review:manage. |
404 | No such review in your organization, or no such item in the review. |
Schedules
A schedule describes a recurring review. Schedules are stored but nothing runs them.
| Field | Type | Description |
|---|---|---|
review_type | string | USER_ACCESS, ROLE_MEMBERS, WORKSPACE or FULL_ORG. |
frequency_days | integer | How often the review should happen. |
scope_type, scope_id | string | Optional. |
reviewer_user_id | string | The person expected to do the review. |
next_review_date | string | Timestamp of the next review. |
is_enabled | boolean | Default true. |
The response adds id (ars_...), organization_id, last_review_date, created_at and updated_at.
curl -X POST https://api.hexelstudio.com/v1/reviews/schedules \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"review_type": "FULL_ORG",
"frequency_days": 90,
"reviewer_user_id": "<user-id>",
"next_review_date": "2027-01-15T00:00:00Z"
}'| Call | Permission | Response |
|---|---|---|
POST /v1/reviews/schedules | iam:review:write | 201 with the schedule |
GET /v1/reviews/schedules | iam:review:read | Array of your organization's schedules |
DELETE /v1/reviews/schedules/{schedule_id} | iam:review:write | 204, or 404 |
There is no endpoint to update a schedule. Delete it and create a new one.