Review events
Review events tell your systems that review work started, moved, or finished. They’re delivered to the webhook endpoints you set up in Settings → Integrations → Webhook delivery. For setup, verification, retries, and signing, see Webhooks.
The same catalog is published as an AsyncAPI 3 document you can load into your own tooling.
Review events report what happened. Don’t resume paused work because of one: your application gets its outcome through the signed outcome callback. Nothing your endpoint sends back records a decision. Reviewers decide in VeriProof.
Event fields
Every review event has the same fields, in camelCase JSON. A field with no
value is sent as null.
| Field | Type | What it holds |
|---|---|---|
id | string | The event ID, such as evt_7c1d…. Retries and resends keep it, and it matches the webhook-id header. |
type | string | One of the review event types. |
schemaVersion | integer | The payload version. It’s 1 today. |
time | string | When the change happened, as an ISO 8601 timestamp. |
tenantId | string | Your VeriProof organization. |
applicationId | string | The application that asked for review. |
sessionId | string | The session the review belongs to. |
reviewCaseId | string | The review case. |
caseVersion | integer | The case version after this change. |
eventSequence | integer | This event’s position among the case’s events. It only goes up. |
decision | string or null | The outcome, once there is one: approved, rejected, follow_up_required, response_submitted, cancelled, or expired. Reviewer comments are never included. |
reviewTier | string | standard, high, or urgent. |
dueAt | string | When the review is due. |
expiresAt | string | When the review expires if no one decides. |
reasonCategory | string | A short fixed reason. See Reason categories. |
presentationDigest | string or null | SHA-256 (hex) of what the assigned reviewer is shown. null until the case has an assignment. |
caseUrl | string | The application API URL for the case, …/v1/review-cases/{reviewCaseId}. Read it with an application key. |
Events don’t include prompts, model output, tool payloads, reviewer comments,
or personal data. When your integration needs more, read caseUrl with an
application key that’s allowed to see it. You always get the case as it is now,
never a stale copy from an old event.
Review event types
Choose any of these for an endpoint.
| Type | Sent when | decision |
|---|---|---|
review.case.created | A policy result opens a review case. | null |
review.case.assignment_changed | The case is claimed or moves to a different reviewer. | null |
review.case.follow_up_required | The reviewer asks for more information before deciding. | follow_up_required |
review.case.decided | The reviewer records a decision. | approved, rejected, or response_submitted |
review.case.cancelled | The case is cancelled before a decision. | cancelled |
review.case.expired | The case reaches expiresAt without a decision. | expired |
review.case.overdue | The case passes dueAt and is still open. | The current value, usually null |
review.continuation.acknowledged | Your application reports what it did with the outcome. | The case’s decision |
review.continuation.delivery_failed | VeriProof gives up sending the outcome callback to your application. | The case’s decision |
The examples below follow one case from start to finish, then show the other ways a case can end.
review.case.created
A new case, not yet assigned. reasonCategory says why review was needed.
{
"id": "evt_4f9a2c7e1b0d8a35c6e2f14b9d07a8c3e5f1b2d4",
"type": "review.case.created",
"schemaVersion": 1,
"time": "2026-09-30T14:02:11.482+00:00",
"tenantId": "3f2b8c1e-7d4a-4e9b-9c61-2a5d0e8f7b13",
"applicationId": "9d6e1a47-2c3b-4f58-a0e9-7b1c5d2f8e60",
"sessionId": "c2a7f0d9-4e1b-4c36-8d5a-0f9e3b6a1c72",
"reviewCaseId": "71b3e5d2-8a0c-4f6e-9b14-2d7c0a5e3f98",
"caseVersion": 1,
"eventSequence": 1,
"decision": null,
"reviewTier": "high",
"dueAt": "2026-09-30T18:02:11.482+00:00",
"expiresAt": "2026-10-01T14:02:11.482+00:00",
"reasonCategory": "high_risk_action",
"presentationDigest": null,
"caseUrl": "https://ingest.veriproof.app/v1/review-cases/71b3e5d2-8a0c-4f6e-9b14-2d7c0a5e3f98"
}review.case.assignment_changed
A reviewer claimed the case, or it moved to someone else. reasonCategory is
claimed or reassigned. If you own reviewer notification,
this is your cue to notify the new reviewer.
{
"id": "evt_0b8e3d61a7c24f95e1d0b3a6c8f27e4d19a5c0b7",
"type": "review.case.assignment_changed",
"schemaVersion": 1,
"time": "2026-09-30T14:05:40.117+00:00",
"tenantId": "3f2b8c1e-7d4a-4e9b-9c61-2a5d0e8f7b13",
"applicationId": "9d6e1a47-2c3b-4f58-a0e9-7b1c5d2f8e60",
"sessionId": "c2a7f0d9-4e1b-4c36-8d5a-0f9e3b6a1c72",
"reviewCaseId": "71b3e5d2-8a0c-4f6e-9b14-2d7c0a5e3f98",
"caseVersion": 2,
"eventSequence": 2,
"decision": null,
"reviewTier": "high",
"dueAt": "2026-09-30T18:02:11.482+00:00",
"expiresAt": "2026-10-01T14:02:11.482+00:00",
"reasonCategory": "claimed",
"presentationDigest": "5d41c0a2e9b7f3186a4c2d9e0f7b1a3c8e6d5f4b2a1c0e9d8f7a6b5c4d3e2f1a",
"caseUrl": "https://ingest.veriproof.app/v1/review-cases/71b3e5d2-8a0c-4f6e-9b14-2d7c0a5e3f98"
}review.case.overdue
The due time passed and the case is still open. reasonCategory is overdue.
{
"id": "evt_e27c9a0f4b1d6385a2e7c0f9b4d1a6e3c8f0b2d5",
"type": "review.case.overdue",
"schemaVersion": 1,
"time": "2026-09-30T18:02:30.004+00:00",
"tenantId": "3f2b8c1e-7d4a-4e9b-9c61-2a5d0e8f7b13",
"applicationId": "9d6e1a47-2c3b-4f58-a0e9-7b1c5d2f8e60",
"sessionId": "c2a7f0d9-4e1b-4c36-8d5a-0f9e3b6a1c72",
"reviewCaseId": "71b3e5d2-8a0c-4f6e-9b14-2d7c0a5e3f98",
"caseVersion": 2,
"eventSequence": 3,
"decision": null,
"reviewTier": "high",
"dueAt": "2026-09-30T18:02:11.482+00:00",
"expiresAt": "2026-10-01T14:02:11.482+00:00",
"reasonCategory": "overdue",
"presentationDigest": "5d41c0a2e9b7f3186a4c2d9e0f7b1a3c8e6d5f4b2a1c0e9d8f7a6b5c4d3e2f1a",
"caseUrl": "https://ingest.veriproof.app/v1/review-cases/71b3e5d2-8a0c-4f6e-9b14-2d7c0a5e3f98"
}review.case.follow_up_required
The reviewer needs more information before deciding. The case stays open.
{
"id": "evt_9c4b7e2a0d5f1836c9e4b7a2d0f5c1e8a3b6d9f2",
"type": "review.case.follow_up_required",
"schemaVersion": 1,
"time": "2026-09-30T18:20:05.390+00:00",
"tenantId": "3f2b8c1e-7d4a-4e9b-9c61-2a5d0e8f7b13",
"applicationId": "9d6e1a47-2c3b-4f58-a0e9-7b1c5d2f8e60",
"sessionId": "c2a7f0d9-4e1b-4c36-8d5a-0f9e3b6a1c72",
"reviewCaseId": "71b3e5d2-8a0c-4f6e-9b14-2d7c0a5e3f98",
"caseVersion": 3,
"eventSequence": 4,
"decision": "follow_up_required",
"reviewTier": "high",
"dueAt": "2026-09-30T18:02:11.482+00:00",
"expiresAt": "2026-10-01T14:02:11.482+00:00",
"reasonCategory": "high_risk_action",
"presentationDigest": "5d41c0a2e9b7f3186a4c2d9e0f7b1a3c8e6d5f4b2a1c0e9d8f7a6b5c4d3e2f1a",
"caseUrl": "https://ingest.veriproof.app/v1/review-cases/71b3e5d2-8a0c-4f6e-9b14-2d7c0a5e3f98"
}review.case.decided
The reviewer recorded a decision in VeriProof. Your application gets the outcome through its outcome callback, not through this event.
{
"id": "evt_3a6d9f0c2e5b8147a3d6f9c0e2b5a8d1f4c7e0b3",
"type": "review.case.decided",
"schemaVersion": 1,
"time": "2026-09-30T19:11:52.761+00:00",
"tenantId": "3f2b8c1e-7d4a-4e9b-9c61-2a5d0e8f7b13",
"applicationId": "9d6e1a47-2c3b-4f58-a0e9-7b1c5d2f8e60",
"sessionId": "c2a7f0d9-4e1b-4c36-8d5a-0f9e3b6a1c72",
"reviewCaseId": "71b3e5d2-8a0c-4f6e-9b14-2d7c0a5e3f98",
"caseVersion": 4,
"eventSequence": 5,
"decision": "approved",
"reviewTier": "high",
"dueAt": "2026-09-30T18:02:11.482+00:00",
"expiresAt": "2026-10-01T14:02:11.482+00:00",
"reasonCategory": "high_risk_action",
"presentationDigest": "5d41c0a2e9b7f3186a4c2d9e0f7b1a3c8e6d5f4b2a1c0e9d8f7a6b5c4d3e2f1a",
"caseUrl": "https://ingest.veriproof.app/v1/review-cases/71b3e5d2-8a0c-4f6e-9b14-2d7c0a5e3f98"
}review.continuation.acknowledged
Your application told VeriProof what it did with the outcome. reasonCategory
is what happened: resumed, terminated, remained_paused, superseded, or
failed.
{
"id": "evt_d8f1b4a7c0e3962d8f1b4a7c0e3d6a9f2c5e8b1d",
"type": "review.continuation.acknowledged",
"schemaVersion": 1,
"time": "2026-09-30T19:12:03.208+00:00",
"tenantId": "3f2b8c1e-7d4a-4e9b-9c61-2a5d0e8f7b13",
"applicationId": "9d6e1a47-2c3b-4f58-a0e9-7b1c5d2f8e60",
"sessionId": "c2a7f0d9-4e1b-4c36-8d5a-0f9e3b6a1c72",
"reviewCaseId": "71b3e5d2-8a0c-4f6e-9b14-2d7c0a5e3f98",
"caseVersion": 5,
"eventSequence": 7,
"decision": "approved",
"reviewTier": "high",
"dueAt": "2026-09-30T18:02:11.482+00:00",
"expiresAt": "2026-10-01T14:02:11.482+00:00",
"reasonCategory": "resumed",
"presentationDigest": "5d41c0a2e9b7f3186a4c2d9e0f7b1a3c8e6d5f4b2a1c0e9d8f7a6b5c4d3e2f1a",
"caseUrl": "https://ingest.veriproof.app/v1/review-cases/71b3e5d2-8a0c-4f6e-9b14-2d7c0a5e3f98"
}Sequence numbers can skip. VeriProof records steps that don’t produce a public event, such as the callback receipt between these two events.
review.continuation.delivery_failed
VeriProof couldn’t deliver the outcome callback to your application and stopped trying. Repair the callback endpoint, then replay the delivery from the case. See Operate application callbacks.
{
"id": "evt_6b0e3c9f2a5d8174b0e3c9f2a5d8e1b4c7f0a3d6",
"type": "review.continuation.delivery_failed",
"schemaVersion": 1,
"time": "2026-10-03T19:12:00.915+00:00",
"tenantId": "3f2b8c1e-7d4a-4e9b-9c61-2a5d0e8f7b13",
"applicationId": "9d6e1a47-2c3b-4f58-a0e9-7b1c5d2f8e60",
"sessionId": "5e8a1d3c-0b7f-4a29-9c6e-3f1d8b2a7e05",
"reviewCaseId": "0e4c8b2a-6d1f-4935-a7c0-9b3e5d1f7a24",
"caseVersion": 4,
"eventSequence": 6,
"decision": "rejected",
"reviewTier": "standard",
"dueAt": "2026-09-30T22:40:00.000+00:00",
"expiresAt": "2026-10-01T18:40:00.000+00:00",
"reasonCategory": "callback_delivery_failed",
"presentationDigest": "a9c3e1f7b5d2084c6e1a9f3b7d5c2e0a8f6b4d1c9e7a5f3b1d0c8e6a4f2b9d7c",
"caseUrl": "https://ingest.veriproof.app/v1/review-cases/0e4c8b2a-6d1f-4935-a7c0-9b3e5d1f7a24"
}review.case.cancelled
The case was cancelled before anyone decided, for example because the person
using your application backed out. reasonCategory says why.
{
"id": "evt_f5a8c1e4b7d0293f5a8c1e4b7d0a3c6f9e2b5d8a",
"type": "review.case.cancelled",
"schemaVersion": 1,
"time": "2026-09-30T15:47:18.530+00:00",
"tenantId": "3f2b8c1e-7d4a-4e9b-9c61-2a5d0e8f7b13",
"applicationId": "9d6e1a47-2c3b-4f58-a0e9-7b1c5d2f8e60",
"sessionId": "8f2d6a0c-3e9b-4d17-b5a8-1c7e4f0a9d36",
"reviewCaseId": "2c9a5e1d-7f3b-4082-86d4-a0e7c3b9f512",
"caseVersion": 3,
"eventSequence": 3,
"decision": "cancelled",
"reviewTier": "standard",
"dueAt": "2026-09-30T19:30:00.000+00:00",
"expiresAt": "2026-10-01T15:30:00.000+00:00",
"reasonCategory": "end_user_cancelled",
"presentationDigest": "c7e5a3f1d9b8062e4c7a5f3d1b9e8c6a4f2d0b7e5c3a1f9d8b6e4c2a0f7d5b3e",
"caseUrl": "https://ingest.veriproof.app/v1/review-cases/2c9a5e1d-7f3b-4082-86d4-a0e7c3b9f512"
}review.case.expired
No one decided before expiresAt. An expired case never counts as approval.
{
"id": "evt_1c4f7a0d3b6e9258c1f4a7d0b3e6c9f2a5d8b1e4",
"type": "review.case.expired",
"schemaVersion": 1,
"time": "2026-10-01T09:00:00.212+00:00",
"tenantId": "3f2b8c1e-7d4a-4e9b-9c61-2a5d0e8f7b13",
"applicationId": "9d6e1a47-2c3b-4f58-a0e9-7b1c5d2f8e60",
"sessionId": "b4e0a7c3-1d9f-4658-8e2b-6a3c0f9d5e71",
"reviewCaseId": "e8d2b6f0-4a1c-4739-9e5b-3c7a1f8d0b46",
"caseVersion": 3,
"eventSequence": 4,
"decision": "expired",
"reviewTier": "urgent",
"dueAt": "2026-10-01T08:00:00.000+00:00",
"expiresAt": "2026-10-01T09:00:00.000+00:00",
"reasonCategory": "expired",
"presentationDigest": "e1b9d7f5c3a2186e0b9d7f5c3a1e8b6d4f2c0a9e7b5d3f1c8a6e4b2d0f9c7a5e",
"caseUrl": "https://ingest.veriproof.app/v1/review-cases/e8d2b6f0-4a1c-4739-9e5b-3c7a1f8d0b46"
}Reason categories
reasonCategory is always one of these values. Treat a value you don’t
recognize as other.
| Group | Values |
|---|---|
| Why review was needed | policy_requires_human_review, high_risk_action, sensitive_data |
| Who has the case | claimed, reassigned |
| Why the case was cancelled | end_user_cancelled, parent_workflow_ended, safer_alternative_completed, checkpoint_unavailable, checkpoint_superseded, application_lifecycle, policy_rule_withdrawn |
| Time | overdue, expired |
| What your application did | resumed, terminated, remained_paused, superseded, failed |
| Outcome callback | callback_delivery_failed |
| Anything else | other |
Subscription events
Every endpoint receives these events about the subscription itself. You can’t
turn them off, and they go out even while the endpoint isn’t receiving review
events. They share id, type, schemaVersion, time, and tenantId with
review events, and add subscriptionId plus the fields below.
| Type | Sent when | Extra fields |
|---|---|---|
subscription.verification_requested | The endpoint is created, its URL or signing method changes, it’s turned back on after VeriProof turned it off, or someone sends verification again. | challenge |
subscription.disabled | An event kept failing for three days and VeriProof turned the endpoint off. | reason |
subscription.signing_key_rotating | Someone rotated the endpoint’s whsec_ secret. The old secret keeps signing for 24 hours. | previousSecretValidUntil |
subscription.events_missed | The endpoint starts receiving again after being off. | missedFrom, missedUntil |
reason is endpoint_gone or retries_exhausted. In practice you’ll see
retries_exhausted: after a 410 Gone, VeriProof sends nothing more to that
endpoint.
Answer subscription.verification_requested with a 2xx status and
{"challenge": "<the same value>"}, as shown in
Webhooks.
{
"id": "evt_0192a3f5d8e07b1c9a2d4e6f8b0c1d3e",
"type": "subscription.disabled",
"schemaVersion": 1,
"time": "2026-10-04T02:15:09.644+00:00",
"tenantId": "3f2b8c1e-7d4a-4e9b-9c61-2a5d0e8f7b13",
"subscriptionId": "a41c7e92-5b3d-4f08-8e6a-1d9c2b7f0e54",
"reason": "retries_exhausted"
}{
"id": "evt_0192a3f6a1b27c3d8e4f5a6b7c8d9e0f",
"type": "subscription.signing_key_rotating",
"schemaVersion": 1,
"time": "2026-10-05T10:00:00.000+00:00",
"tenantId": "3f2b8c1e-7d4a-4e9b-9c61-2a5d0e8f7b13",
"subscriptionId": "a41c7e92-5b3d-4f08-8e6a-1d9c2b7f0e54",
"previousSecretValidUntil": "2026-10-06T10:00:00.000+00:00"
}{
"id": "evt_0192a3f7b2c38d4e9f5a6b7c8d9e0f1a",
"type": "subscription.events_missed",
"schemaVersion": 1,
"time": "2026-10-04T16:40:27.318+00:00",
"tenantId": "3f2b8c1e-7d4a-4e9b-9c61-2a5d0e8f7b13",
"subscriptionId": "a41c7e92-5b3d-4f08-8e6a-1d9c2b7f0e54",
"missedFrom": "2026-10-04T02:15:09.644+00:00",
"missedUntil": "2026-10-04T16:40:27.318+00:00"
}Handle events safely
Delivery is at least once, with no ordering guarantee. A receiver that follows these rules stays correct when events repeat or arrive late:
- Verify the signature before reading the body. See Verify a request.
- Store the event under its
id. If you already have thatid, answer 2xx and stop. - For each
reviewCaseId, keep the highestcaseVersion, theneventSequence, you’ve seen. Treat an event below that as history, not as the current state. - Answer with a 2xx status within 10 seconds, then do the slower work.
- When you need details, read
caseUrlwith an application key rather than relying on the event body.
Ignore fields you don’t recognize. When the payload shape changes,
schemaVersion changes with it; event type names stay the same.
Catch up after an outage
If your endpoint was down or turned off, list the review events VeriProof sent, or tried to send, to that subscription:
GET /v1/review-events?subscriptionId={subscriptionId}&page=1&pageSize=50Call it on the Customer Portal API as someone with the Manage Integrations
permission. Results are newest first. pageSize defaults to 20 and goes up to
100.
{
"subscriptionId": "a41c7e92-5b3d-4f08-8e6a-1d9c2b7f0e54",
"events": [
{
"event": {
"id": "evt_3a6d9f0c2e5b8147a3d6f9c0e2b5a8d1f4c7e0b3",
"type": "review.case.decided",
"schemaVersion": 1,
"…": "the same fields as the delivered event"
},
"deliveryStatus": "dead_lettered",
"deliveredAt": null
}
],
"totalCount": 1,
"page": 1,
"pageSize": 50,
"hasMore": false
}deliveryStatus is pending, delivering, retry_scheduled, delivered, or
dead_lettered (failed). Run each event through the same handler you use for
deliveries; your de-duplication skips the ones you already have.
The list covers events that were sent, or retried and failed, while your
endpoint was down. Events from a time the subscription was turned off were
never sent to it, so they aren’t listed. Use the missedFrom and missedUntil
window from subscription.events_missed to check review work from that period
in VeriProof.
This is a recovery read. Don’t poll it to find new work; the webhook is how VeriProof tells you about new events.
Session and platform events
An endpoint can also receive session, application, health, and compliance
events, such as session.completed, application.promoted,
health.status_changed, and compliance.rule_violation. Those keep their own
format: id, type, timestamp, apiVersion, customerId, application,
and data. They’re signed and delivered the same way as review events.