Skip to main content
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 four webhook kinds:
  • profile_status_updated is sent by Minerva screening and fires when a screening profile 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.
  • custom_list_deployment is sent by Minerva custom lists and fires when a custom list deployment changes between pending, live, and down in a workspace where the list is active.
A single destination can subscribe to any combination of the four 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 page walks through the UI. Configuration is also available through the API at https://api.gominerva.com/webhooks/v2/screening. Despite the screening in the path, it manages all four kinds: Create screening webhook. Webhooks have a workspace or tenant scope. Workspace is the default. A workspace-scoped destination receives events from one workspace; a tenant-scoped destination receives events from every workspace in your organization, including workspaces you create later. An admin application key can manage both scopes. 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. Tenant-scoped destinations have a separate limit of the same size. Enterprise customers can request more.

The envelope

Every delivery uses the same envelope:
  • webhookId is the destination’s unique ID.
  • webhookName is the name you assigned, echoed back so you can route deliveries.
  • tenantId and workspaceId appear at the top level on every event kind. They name the organization and workspace where the event occurred, even when the destination has tenant scope. These are additive fields; ignore fields you do not recognize. The per-kind event.meta objects are unchanged.
  • event.kind is profile_status_updated, idv, onboarding, or custom_list_deployment.
  • 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.

Workspace and tenant scope

Every destination record includes scope, either workspace or tenant. Omitting scope when creating a destination selects workspace. Workspace records include workspaceId; tenant records omit it, rather than returning null.

Access and workspace selection

Send x-workspace-id to select the workspace for workspace-scoped destinations. Without it, a workspace-scoped key uses its own workspace; a tenant-scoped key uses your organization’s default workspace. The header never narrows the events received by a tenant-scoped destination. An x-workspace-id that does not name a workspace in your organization returns 404, and a value that is not a valid workspace ID returns 400. Workspace-scoped and tenant-scoped keys created on the Developers page have no webhook permissions. With one of those keys, these endpoints return 403 with the message “Missing or insufficient permissions.” The message does not say that the key type is the problem. To manage destinations through the API, use a Live admin application key. You can also manage them on the Developers page. Admin application keys are Live only. Tenant-scoped destinations must be enabled for your organization; creating or editing one before enablement returns 400.

Create, list, edit, and move destinations

Use https://api.gominerva.com/webhooks/v2/screening: Moving a destination preserves its id and webhookKey. To move it into a workspace, send scope: "workspace" and select the destination workspace with x-workspace-id. Moving in either direction requires a Live admin application key or a dashboard Owner/Admin. A move also checks the destination scope’s limit.

One receiver for all client workspaces

If you run one workspace per client, register a tenant-scoped destination once, before creating the client workspaces:
cURL
Then create each client’s workspace and profiles. No new destination is needed as you add workspaces. Route incoming events using the top-level workspaceId and validate the destination’s X-Webhook-Key. A tenant destination and a workspace destination that subscribe to the same event at the same URL receive two logical deliveries. They have different webhook IDs and keys. Do not dedupe by URL or profile ID: use X-Webhook-Delivery-ID where supplied, as described in Delivery, retries, and dedupe. For custom list deployment events, delivery-target changes can take up to 60 seconds to propagate because targets are cached. This is not a propagation guarantee for other event kinds.

Profile status updates (profile_status_updated)

Fires when a screening profile changes status. It also fires when a monitoring screening of a profile that is already potential_match returns material matches: matches that alert suppression did not suppress and that are not already marked false positive. See When profile_status_updated fires. Subscribable values: 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:
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.
Example delivery:

When profile_status_updated fires

A delivery is sent in three cases:
  • A screening changes the profile’s status, for example from no_flags to potential_match.
  • A screening of a profile that is already potential_match returns material matches. This is usually a monitoring screening, and it also applies when the profile is onboarded again. The status stays the same, but the delivery tells you the latest screening still has material findings. Material matches are matches that repeated alert suppression did not suppress and that are not already marked false positive. With suppression off, every repeat of an open match is material. A monitoring screening with every match suppressed sends no delivery.
  • A reviewer or an API call updates the profile. This sends a delivery even when the status did not change.
These three meta fields are available on profile status deliveries and tell these cases apart: Read trigger together with searchId, not just event.value or the profile’s accumulated flags. A profile can stay potential_match and retain earlier flags when the latest screening has no material matches, because every match was suppressed or marked false positive. Its lastSearchId can advance while lastFlagSearchId still refers to an earlier screening. An onboarding delivery labeled status_change can also retain potential_match because a clean screening does not automatically clear an earlier flagged status. That delivery is not evidence of new material findings. A rescreen delivery means the current screening has effective flags from material matches: the profile’s lastFlagSearchId identifies the same screening as searchId. It does not mean every match is new. To list what that screening found, see What changed since the last run.
  • Ignore meta fields you do not recognize. Minerva adds fields to this object over time.
  • Dedupe on the X-Webhook-Delivery-ID header, which stays the same across retries of one delivery. See Delivery, retries, and dedupe.

Identity verification lifecycle (idv)

Fires as an IDV session moves through capture, assessment, and review. The 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):
Use meta.sessionId, which is stable for the life of the session, to correlate a session’s deliveries, and meta.externalId to join a delivery to your own system’s reference for the profile. The onboarding webhook carries externalId as well.
Example delivery:

Correlating events with sessions

Every idv event for a session carries the same sessionId: it is the id returned by POST /sessions and used in GET /sessions/{sessionId}, and it does not change for the life of the session. When the session was created from a profile with an externalId, every event for the session carries it as well, mirroring the reference from your system. Use sessionId to correlate a session’s events, and externalId to route them to your own customer record. Both are stable identifiers and safe to use as join keys; delivery retry and dedupe semantics are in Delivery, retries, and dedupe.

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: Example delivery:
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.

Custom list deployments (custom_list_deployment)

Fires when a custom list’s deployment status changes, so your integration can stop polling the status endpoint after it publishes a version. The values use the same public status vocabulary as that endpoint, so a delivery’s status is what the status endpoint would have returned at that moment. Subscribable values: This kind requires custom lists to be enabled for your organization. Creating a webhook with this kind, or changing its values, returns 400 with “Custom list webhooks require custom lists to be enabled for this organization. Contact support@gominerva.com to enable custom lists.” when they are not. Minerva sends a delivery only when the status actually changes:
  • A list that stays live sends nothing more, however long it serves.
  • A list’s first deployment sends no pending, because a new list is already pending. You receive live or down when the deployment finishes.
  • Archiving a list sends nothing, including when someone archives it from the dashboard. To detect that, read the list with GET /{listId}.
Each delivery goes to the webhooks of every workspace where the list is active or being activated. A webhook in a workspace where the list is not active receives nothing for that list, so create the webhook in each workspace where you activate the list. The meta object for this kind: The failure object never carries internal diagnostics. failure.stage is one of these values, each with a fixed failure.message: Example delivery:
A down delivery carries the failure summary:
Publishing one list briefly moves the organization’s other live lists to pending while Minerva redistributes them, then back to live. Each of those lists sends a pending and then a live delivery with an unchanged versionId. Treat them as expected, not as a new deployment.
Deliveries can arrive out of order: a retried pending can arrive after the live that followed it. Order deliveries for a list and workspace by meta.occurredAt, use meta.previousStatus to spot a gap, and when in doubt reconcile with one call to the status endpoint.

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, onboarding, and custom list deployment kinds also send X-Webhook-Delivery-ID, an opaque ID that is stable for each delivery and destination, so a retry repeats it. It is a 64-character hexadecimal string. 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, onboarding, and custom_list_deployment 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.
  • For custom_list_deployment, a 3xx response or any other 4xx response, including 401 or 403 from a receiver that rejects the key, is permanent and is not retried. Redirects are never followed. A change that Minerva cannot start delivering within an hour is dropped rather than sent late; reconcile with the status endpoint if you suspect a missed delivery.
  • For custom_list_deployment, a destination that resolves to a non-public address is refused. That failure is permanent and is not retried.
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, onboarding, and custom_list_deployment, 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 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 and custom_list_deployment deliveries, Minerva checks the destination and refuses loopback, private, link-local, and cloud metadata addresses; idv also 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.