> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gominerva.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Receive near-real-time notifications for screening profile updates, identity verification lifecycle events, and profile onboarding runs, with payloads, retries, and dedupe guidance.

Minerva webhooks notify your downstream systems when something changes in your workspace. Each delivery is a JSON POST to an endpoint you control, naming the webhook, the event kind, the event value, and a `meta` object with reviewer-safe context.

There are three webhook kinds:

* `profile_status_updated` is sent by Minerva screening and fires when a screening [profile](/minerva-profiles) changes status, for example when findings appear or a reviewer decides.
* `idv` is sent by Minerva identity verification and fires as a session progresses; an artifact is captured, an assessment completes, or a reviewer decides.
* `onboarding` is sent by Minerva screening and fires when a combined screening and identity verification onboarding run changes state or completes.

A single destination can subscribe to one, two, or all three kinds. Your receiver tells them apart by `event.kind`.

## Configure webhooks

Create and manage destinations from the Developers page (**Administration** > **Developers**) in the Minerva dashboard. The [API Keys](/api-reference/api-keys) page walks through the UI. Configuration is also available through the Admin API, where it lives under `/v2/webhooks/screening` (a historical path that manages all three kinds): [Create screening webhook](https://docs.gominerva.com/api-reference/screening-webhooks/create-screening-webhook).

Each destination holds a name, a destination URL, an event subscription list, and a webhook key. Each workspace can keep up to 10 destinations by default. Enterprise customers can request more.

## The envelope

Every delivery uses the same envelope:

```json theme={null}
{
  "webhookId": "ecc9b161-e6bf-48e4-816d-b13046a42eaa",
  "webhookName": "Customer onboarding updates",
  "event": {
    "kind": "onboarding",
    "value": "profile_onboarding.completed",
    "meta": { "...": "..." }
  }
}
```

* `webhookId` is the destination's unique ID.
* `webhookName` is the name you assigned, echoed back so you can route deliveries.
* `event.kind` is `profile_status_updated`, `idv`, or `onboarding`.
* `event.value` is the specific event that fired (see the per-kind sections below).
* `event.meta` carries the event context. Its fields and value types differ per kind; `idv` delivers every value as a string.

## Profile status updates (profile\_status\_updated)

Fires when a screening profile changes status. Subscribable values:

| Value             | Meaning                                                                                         |
| ----------------- | ----------------------------------------------------------------------------------------------- |
| `potential_match` | Screening found one or more qualifying findings, or a verification session was held for review. |
| `accepted`        | A reviewer accepted the profile.                                                                |
| `rejected`        | A reviewer rejected the profile.                                                                |

`escalation` and `in_review` are not subscribable: a profile moving into those states does not trigger a delivery of this kind.

The `meta` object for this kind:

| Key               | Type    | Description                                                                                                                                                                                                                               |
| ----------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `latestFlags`     | array   | The profile's latest flags, each `{name, count, label, metadata?}`. Flag metadata can include `source`, `category`, `severity`, `lastSessionId`, `lastFlaggedSessionId`, `lastAssessedAt`, `lastFlaggedAt`, `reviewStatus`, and `gating`. |
| `profileId`       | string  | The Minerva profile ID.                                                                                                                                                                                                                   |
| `externalId`      | string  | Your external reference for the profile. May be empty.                                                                                                                                                                                    |
| `workspaceId`     | string  | The workspace scope.                                                                                                                                                                                                                      |
| `archived`        | boolean | Whether the profile is archived.                                                                                                                                                                                                          |
| `monitoredStatus` | string  | The profile's monitoring status, for example `monitored`.                                                                                                                                                                                 |

<Note>
  The status that changed is `event.value`; there is no `status` field in the
  `meta` object for this kind. This kind's `meta` also carries no `tenantId`.
</Note>

Example delivery:

```json theme={null}
{
  "webhookId": "ecc9b161-e6bf-48e4-816d-b13046a42eaa",
  "webhookName": "Screening status updates",
  "event": {
    "kind": "profile_status_updated",
    "value": "potential_match",
    "meta": {
      "latestFlags": [
        {
          "name": "screeningSanctionsMatch",
          "count": 2,
          "label": "Sanctions Match",
          "metadata": {
            "source": "Sanctions",
            "severity": "high",
            "gating": true,
            "reviewStatus": "requires_review",
            "lastFlaggedAt": "2026-09-25T18:40:00Z"
          }
        }
      ],
      "profileId": "66c391b92888a0db5cc6d3f6",
      "externalId": "customer-123",
      "workspaceId": "workspace-a1b2c3",
      "archived": false,
      "monitoredStatus": "monitored"
    }
  }
}
```

## Identity verification lifecycle (idv)

Fires as an IDV session moves through capture, assessment, and review. The [IDV Integration Guide](/api-reference/idv-integration-guide) covers the full session lifecycle. Subscribable values:

* `idv.document.captured` fires when a capture image or supporting document finishes uploading; `meta.capturedKind` names the artifact kind, for example `id_front`.
* `idv.session.completed` fires when an assessment reaches a terminal state, alongside exactly one of `idv.session.approved`, `idv.session.failed`, or `idv.session.requires_review`.
* `idv.session.approved` fires when the session passed without a human decision (`meta.reviewStatus` is `automatic_pass`) or a reviewer accepted it (`meta.reviewStatus` is `accepted`).
* `idv.session.requires_review` fires when the assessment refused to auto-decide and a reviewer must look at it.
* `idv.session.rejected` fires when a reviewer rejected the session. A model rejection never sends this value; it arrives as `idv.session.requires_review` instead.
* `idv.session.failed` fires when the assessment itself failed.

Review states visible to your backend on a session:

* `automatic_pass` is a clean pass with no human review.
* `requires_review` means the service held the session for a human decision.
* `escalation` means a reviewer bumped it up while keeping it open.
* `accepted` and `rejected` are terminal human decisions.

The `meta` object for this kind. **All values are strings**, so booleans arrive quoted (`"true"`, not `true`):

| Key                           | Type   | Description                                                                                                   |
| ----------------------------- | ------ | ------------------------------------------------------------------------------------------------------------- |
| `sessionId`                   | string | The IDV session ID.                                                                                           |
| `tenantId`                    | string | The tenant scope.                                                                                             |
| `workspaceId`                 | string | The workspace scope.                                                                                          |
| `profileId`                   | string | The linked Minerva profile ID.                                                                                |
| `status`                      | string | The session's flow status at the time of the event, for example `assessed` or `failed`.                       |
| `reviewStatus`                | string | The review status at the time of the event, for example `automatic_pass` or `accepted`.                       |
| `occurredAt`                  | string | The UTC ISO 8601 timestamp of the transition.                                                                 |
| `livenessChecked`             | string | `"true"` or `"false"`. Which checks ran on the assessment; all checks are `"false"` on non-assessment events. |
| `documentAuthenticityChecked` | string | `"true"` or `"false"`.                                                                                        |
| `faceMatchPerformed`          | string | `"true"` or `"false"`.                                                                                        |
| `capturedKind`                | string | Present only on `idv.document.captured`, for example `id_front`.                                              |

<Warning>
  `idv` deliveries carry no `externalId`. To map a session back to your own
  customer reference, look up the profile by `profileId`.
</Warning>

Example delivery:

```json theme={null}
{
  "webhookId": "3b4d2f10-9cc2-4b8e-9ef1-5be2a17d0c44",
  "webhookName": "IDV lifecycle",
  "event": {
    "kind": "idv",
    "value": "idv.session.requires_review",
    "meta": {
      "sessionId": "idv-2cdf8b5c8b3db8a1d6f5d4ce9d46a63f",
      "tenantId": "tenant-9f3a",
      "workspaceId": "workspace-a1b2c3",
      "profileId": "66c391b92888a0db5cc6d3f6",
      "status": "assessed",
      "reviewStatus": "requires_review",
      "occurredAt": "2026-09-25T18:45:12Z",
      "livenessChecked": "true",
      "documentAuthenticityChecked": "true",
      "faceMatchPerformed": "true"
    }
  }
}
```

## Profile onboarding (onboarding)

Fires for combined screening and identity verification onboarding runs. Subscribable values:

* `profile_onboarding.review_required` fires when the run needs review before it can complete, for example when screening findings appear or an IDV session is held for review.
* `profile_onboarding.completed` fires when the run finishes. The `outcome` field says how: `automatic_pass`, `accepted`, `rejected`, or `escalation`.
* `profile_onboarding.failed` fires when the run failed.

The `meta` object for this kind:

| Key               | Type    | Description                                                                                                                                       |
| ----------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `latestFlags`     | array   | The profile's latest flags, as in `profile_status_updated`.                                                                                       |
| `tenantId`        | string  | The tenant scope.                                                                                                                                 |
| `profileId`       | string  | The Minerva profile ID.                                                                                                                           |
| `externalId`      | string  | Your external reference for the profile. May be empty.                                                                                            |
| `workspaceId`     | string  | The workspace scope.                                                                                                                              |
| `archived`        | boolean | Whether the profile is archived.                                                                                                                  |
| `monitoredStatus` | string  | The profile's monitoring status.                                                                                                                  |
| `profileStatus`   | string  | The profile's status, for example `accepted`.                                                                                                     |
| `status`          | string  | The run's aggregate state: `review_required`, `completed`, or `failed`.                                                                           |
| `outcome`         | string  | How the run resolved, for example `findings_require_review`, `automatic_pass`, `accepted`, `rejected`, `escalation`, or `failed`.                 |
| `phase`           | string  | Which part of the run reported, for example `screening`, `idv`, `profile_status`, or `profile_created`.                                           |
| `onboardingRunId` | string  | The onboarding run ID.                                                                                                                            |
| `screening`       | object  | A summary of the screening component: `{enabled, completed, status?, outcome?, searchId?, flagsCount?}`.                                          |
| `idv`             | object  | A summary of the identity verification component: `{enabled, completed, status?, outcome?, reviewStatus?, sessionId?, workflowId?, flagsCount?}`. |

Example delivery:

```json theme={null}
{
  "webhookId": "ecc9b161-e6bf-48e4-816d-b13046a42eaa",
  "webhookName": "Customer onboarding updates",
  "event": {
    "kind": "onboarding",
    "value": "profile_onboarding.completed",
    "meta": {
      "latestFlags": [
        {
          "name": "screeningSanctionsMatch",
          "count": 2,
          "label": "Sanctions Match"
        }
      ],
      "tenantId": "tenant-9f3a",
      "workspaceId": "workspace-a1b2c3",
      "profileId": "66c391b92888a0db5cc6d3f6",
      "externalId": "customer-123",
      "archived": false,
      "monitoredStatus": "monitored",
      "profileStatus": "accepted",
      "status": "completed",
      "outcome": "accepted",
      "phase": "profile_status",
      "onboardingRunId": "66c391b92888a0db5cc6d3f6",
      "screening": {
        "enabled": true,
        "completed": true,
        "status": "potential_match",
        "outcome": "findings_require_review",
        "searchId": "6a1f8c2d9e4b7a5f3c1d",
        "flagsCount": 2
      },
      "idv": {
        "enabled": true,
        "completed": true,
        "status": "completed",
        "outcome": "automatic_pass",
        "reviewStatus": "automatic_pass",
        "sessionId": "idv-2cdf8b5c8b3db8a1d6f5d4ce9d46a63f",
        "workflowId": "idvw_5f8c2a1b",
        "flagsCount": 0
      }
    }
  }
}
```

<Note>
  A completed run is never reported while an enabled component is still pending,
  and `review_required` can arrive once per run. Deliveries are at-least-once
  and can arrive out of order.
</Note>

## Delivery, retries, and dedupe

Every delivery is a JSON POST with `Content-Type: application/json` and the `X-Webhook-Key` header carrying the destination's webhook key. Screening and onboarding kinds also send `X-Webhook-Delivery-ID`, a stable ID for the delivery that they repeat across retries.

Delivery is at-least-once. A delivery can be attempted more than once and deliveries can arrive out of order, so make your handler idempotent.

**`profile_status_updated` and `onboarding` deliveries:**

* Each dispatch makes up to 3 attempts in process (about 100 ms, then 300 ms between them), retrying on `408`, `429`, `502`, `503`, `504`, and transport errors; a `429` response's `Retry-After` is honored up to 3 seconds.
* A delivery is attempted at most 6 times: 1 inline attempt plus durable retries after 5 minutes, 15 minutes, 45 minutes, 2 hours 15 minutes, and 6 hours 45 minutes, scheduled by a 5-minute sweeper.

**`idv` deliveries:**

* Up to 6 attempts with waits of 30, 60, 120, 240, and 480 seconds.
* Transport errors and `5xx` responses are retried; `3xx` and `4xx` responses are permanent rejections (redirects are never followed), and exhausted deliveries are dead-lettered.

Acknowledge each delivery promptly with a `2xx` response and process it asynchronously. Anything else is treated as a failure and retried or dropped according to the schedule above. Keep the response body small: an `idv` delivery whose response body exceeds 64 KiB is aborted as a policy violation.

Dedupe guidance:

* For `profile_status_updated` and `onboarding`, dedupe on `X-Webhook-Delivery-ID`.
* For `idv`, there is no delivery ID on the wire. Dedupe on the natural event key (`sessionId`, `event.value`, `occurredAt`, plus `capturedKind` for `idv.document.captured`) or make your handler idempotent.

## Test a destination

The Admin API's [test endpoint](https://docs.gominerva.com/api-reference/screening-webhooks/test-screening-webhook) sends a simulated payload to your destination URL so you can confirm the endpoint is reachable and your receiver validates the key. The test send carries only the `X-Webhook-Key` header and generates its payload server-side, so the mock structure mirrors production but the values are not a replay of a real event.

## Security

* Validate `X-Webhook-Key` on every delivery and reject mismatches before processing the body.
* Store the webhook key in a secrets manager, and rotate it by recreating the destination if it leaks.
* Destination URLs must be public HTTPS endpoints you control. For `idv` deliveries, Minerva checks the destination and refuses loopback, private, link-local, and cloud metadata addresses; it sends only to HTTPS endpoints. Screening and onboarding deliveries do not perform that address check, so hold their destinations to the same standard yourself.
* Deliveries are designed to be reviewer-safe: payloads carry IDs, statuses, and counts, not document contents, OCR text, or raw model output. Treat the endpoint itself as sensitive anyway, because the fields are only safe in the context of your own workspace.
