Skip to content

Reviews

Review packets are generated when the Decay Engine determines that an access grant has decayed below the configured threshold. Reviewers evaluate the evidence and decide whether to confirm, revoke, or escalate the access.


ReviewPacket Schema

Field Type Description
id UUID Review packet identifier.
principal_id UUID The principal whose access is under review.
asset_id UUID The asset being reviewed.
access_grant_ids UUID[] Grant IDs included in this review.
decay_score float Decay score at the time the review was generated.
risk_level enum CRITICAL · HIGH · MEDIUM · LOW
evidence_json object Supporting evidence (usage stats, peer comparison, etc.).
recommendation enum System recommendation: REVOKE · REVIEW · CONFIRM
assigned_to_id UUID Verity ID of the assigned reviewer.
workflow_id UUID Associated workflow instance (nullable).
status enum PENDING · DECIDED · EXPIRED · ESCALATED
due_at datetime Review deadline.
created_at datetime When the review packet was generated.

List Reviews

GET /v1/reviews

Retrieve a paginated list of review packets with optional filters.

Query Parameters

Parameter Type Default Description
status string Filter by status: PENDING, DECIDED, EXPIRED, ESCALATED.
risk_level string Filter by risk level: CRITICAL, HIGH, MEDIUM, LOW.
due_before datetime Only return reviews due before this date (ISO 8601).
cursor string Pagination cursor from a previous response.
limit integer 50 Items per page (max 100).

Example Request

curl -s "http://localhost:8000/v1/reviews?status=PENDING&risk_level=HIGH&limit=2" \
  -H "Authorization: Bearer $TOKEN"

Example Response

{
  "items": [
    {
      "id": "019f6b7c-8d9e-7000-8000-000000006001",
      "principal_id": "019f1a2b-3c4d-7000-8000-000000000002",
      "asset_id": "019f3d4e-5f60-7000-8000-000000000100",
      "access_grant_ids": [
        "019f2c3d-4e5f-7000-8000-000000000020"
      ],
      "decay_score": 12.5,
      "risk_level": "HIGH",
      "evidence_json": {
        "days_inactive": 87,
        "last_access": "2025-04-18T10:15:00Z",
        "peer_usage_pct": 0.85,
        "sensitivity": "CONFIDENTIAL",
        "contains_pii": true
      },
      "recommendation": "REVOKE",
      "assigned_to_id": "019f1a2b-3c4d-7000-8000-000000000099",
      "workflow_id": "019f7c8d-9ea0-7000-8000-000000007001",
      "status": "PENDING",
      "due_at": "2025-07-21T23:59:59Z",
      "created_at": "2025-07-14T06:30:00Z"
    },
    {
      "id": "019f6b7c-8d9e-7000-8000-000000006002",
      "principal_id": "019f1a2b-3c4d-7000-8000-000000000005",
      "asset_id": "019f3d4e-5f60-7000-8000-000000000101",
      "access_grant_ids": [
        "019f2c3d-4e5f-7000-8000-000000000050",
        "019f2c3d-4e5f-7000-8000-000000000051"
      ],
      "decay_score": 18.3,
      "risk_level": "HIGH",
      "evidence_json": {
        "days_inactive": 62,
        "last_access": "2025-05-13T08:00:00Z",
        "peer_usage_pct": 0.92,
        "sensitivity": "CONFIDENTIAL",
        "contains_pii": true
      },
      "recommendation": "REVOKE",
      "assigned_to_id": "019f1a2b-3c4d-7000-8000-000000000099",
      "workflow_id": "019f7c8d-9ea0-7000-8000-000000007002",
      "status": "PENDING",
      "due_at": "2025-07-21T23:59:59Z",
      "created_at": "2025-07-14T06:30:00Z"
    }
  ],
  "next_cursor": "eyJpZCI6IjAxOWY2YjdjLThkOWUtNzAwMC04MDAwLTAwMDAwMDAwNjAwMyJ9"
}

Get Review Packet

GET /v1/reviews/{id}

Retrieve a single review packet by its ID.

Path Parameters

Parameter Type Description
id UUID The review packet's UUID.

Example Request

curl -s "http://localhost:8000/v1/reviews/019f6b7c-8d9e-7000-8000-000000006001" \
  -H "Authorization: Bearer $TOKEN"

Example Response

{
  "id": "019f6b7c-8d9e-7000-8000-000000006001",
  "principal_id": "019f1a2b-3c4d-7000-8000-000000000002",
  "asset_id": "019f3d4e-5f60-7000-8000-000000000100",
  "access_grant_ids": [
    "019f2c3d-4e5f-7000-8000-000000000020"
  ],
  "decay_score": 12.5,
  "risk_level": "HIGH",
  "evidence_json": {
    "days_inactive": 87,
    "last_access": "2025-04-18T10:15:00Z",
    "peer_usage_pct": 0.85,
    "sensitivity": "CONFIDENTIAL",
    "contains_pii": true
  },
  "recommendation": "REVOKE",
  "assigned_to_id": "019f1a2b-3c4d-7000-8000-000000000099",
  "workflow_id": "019f7c8d-9ea0-7000-8000-000000007001",
  "status": "PENDING",
  "due_at": "2025-07-21T23:59:59Z",
  "created_at": "2025-07-14T06:30:00Z"
}

Error Responses

Status Description
404 Review packet not found.

Submit Review Decision

POST /v1/reviews/{id}/decide

Submit a decision on a review packet. Requires the verity.reviewer role.

Idempotency required

The Idempotency-Key header is required for this endpoint. Duplicate submissions with the same key return the original response.

Path Parameters

Parameter Type Description
id UUID The review packet's UUID.

Headers

Header Required Description
Idempotency-Key Yes Unique UUID to ensure exactly-once semantics.

Request Body

Field Type Required Description
decision enum Yes CONFIRM · REVOKE · ESCALATE
justification string Yes Free-text explanation for the decision.
next_review_at datetime No Schedule the next review (only for CONFIRM).
escalate_to_id UUID No Reviewer to escalate to (only for ESCALATE).

Example Request — Revoke

curl -s -X POST "http://localhost:8000/v1/reviews/019f6b7c-8d9e-7000-8000-000000006001/decide" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7" \
  -H "Content-Type: application/json" \
  -d '{
    "decision": "REVOKE",
    "justification": "User has not accessed this dataset in 87 days and 85% of peers no longer have this access. Revoking per least-privilege policy."
  }'

Example Response

{
  "id": "019f8d9e-a0b1-7000-8000-000000008001",
  "packet_id": "019f6b7c-8d9e-7000-8000-000000006001",
  "decision": "REVOKE",
  "decided_at": "2025-07-15T14:22:00Z"
}

Example Request — Confirm with Next Review

curl -s -X POST "http://localhost:8000/v1/reviews/019f6b7c-8d9e-7000-8000-000000006002/decide" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: a3b4c5d6-e7f8-9012-3456-789abcdef012" \
  -H "Content-Type: application/json" \
  -d '{
    "decision": "CONFIRM",
    "justification": "User needs quarterly access for SOX audit preparation. Confirmed with data owner.",
    "next_review_at": "2025-10-15T00:00:00Z"
  }'

Example Response

{
  "id": "019f8d9e-a0b1-7000-8000-000000008002",
  "packet_id": "019f6b7c-8d9e-7000-8000-000000006002",
  "decision": "CONFIRM",
  "decided_at": "2025-07-15T14:25:00Z"
}

Example Request — Escalate

curl -s -X POST "http://localhost:8000/v1/reviews/019f6b7c-8d9e-7000-8000-000000006001/decide" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: d4e5f6a7-b8c9-0123-4567-89abcdef0123" \
  -H "Content-Type: application/json" \
  -d '{
    "decision": "ESCALATE",
    "justification": "Unable to determine business need. Escalating to CISO for review.",
    "escalate_to_id": "019f1a2b-3c4d-7000-8000-000000000080"
  }'

Example Response

{
  "id": "019f8d9e-a0b1-7000-8000-000000008003",
  "packet_id": "019f6b7c-8d9e-7000-8000-000000006001",
  "decision": "ESCALATE",
  "decided_at": "2025-07-15T14:28:00Z"
}

Error Responses

Status Description
400 Invalid request body or decision.
401 Missing or invalid Bearer token.
403 Caller does not have the verity.reviewer role.
404 Review packet not found.
409 Decision already submitted (idempotency collision or already decided).

Reassign Reviewer

POST /v1/reviews/{id}/reassign

Reassign a review packet to a different reviewer. Requires the verity.admin role.

Path Parameters

Parameter Type Description
id UUID The review packet's UUID.

Request Body

Field Type Required Description
assigned_to_id UUID Yes Verity ID of the new reviewer.

Example Request

curl -s -X POST "http://localhost:8000/v1/reviews/019f6b7c-8d9e-7000-8000-000000006001/reassign" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "assigned_to_id": "019f1a2b-3c4d-7000-8000-000000000080"
  }'

Example Response

{
  "id": "019f6b7c-8d9e-7000-8000-000000006001",
  "assigned_to_id": "019f1a2b-3c4d-7000-8000-000000000080",
  "status": "PENDING"
}

Error Responses

Status Description
400 Invalid request body.
403 Caller does not have the verity.admin role.
404 Review packet not found.