Webhooks

Receive report, payment and monitoring events instead of polling.

Reports run asynchronously. Webhooks are how you find out they finished.

Configure a destination

POST /api/v1/webhooks/config replaces the configuration wholesale — send the complete enabledEvents list, not a change.

{
  "url": "https://example.com/hooks/verifisha",
  "enabled": true,
  "enabledEvents": ["report.ready", "report.failed", "payment.paid"],
  "secret": "whsec_replace_with_a_long_random_value"
}

The secret is yours to choose, and it is write-only: it is never returned by the API, so keep it in your own secret store.

Events

EventFires when
report.createdA report is created.
report.readyEvery check in a report has finished.
report.failedA report could not complete.
check.completedOne check finished — useful for progressive UI.
check.failedOne check could not complete.
reference.submittedA referee submitted their answers.
monitoring.alert.createdA monitoring run raised an alert.
payment.paidA candidate or third-party payment completed.

Payloads

Each delivery is a JSON object with the event name, when it was raised, and the event's data:

FieldDescription
eventThe event type, e.g. check.completed. Branch on this.
timestampWhen the event was raised, ISO 8601 UTC.
dataThe event payload, shaped by event.

check.completed

{
  "event": "check.completed",
  "timestamp": "2026-09-19T18:42:11.218Z",
  "data": {
    "workspaceId": "7f000001-a0ae-1000-81a0-ae8e05830000",
    "report": {
      "id": "7f000001-a0ae-1d9d-81a0-ae8e05830014",
      "workspaceId": "7f000001-a0ae-1000-81a0-ae8e05830000",
      "status": "PROCESSING",
      "createdAt": "2026-09-19T18:41:58.104Z",
      "selfVerification": false,
      "paymentMode": "SELF",
      "consentStatus": "APPROVED",
      "checksRequested": ["IDENTITY"],
      "checkStack": ["IDENTITY"]
    },
    "subject": {
      "id": "7f000001-a0ae-18be-81a0-ae8e00000000",
      "type": "INDIVIDUAL"
    },
    "check": {
      "id": "7f000001-a0ae-2222-81a0-ae8e05830014",
      "resultId": "7f000001-a0ae-2222-81a0-ae8e05830014",
      "definitionId": "7f000001-a077-1c27-81a0-774c84700000",
      "type": "IDENTITY",
      "name": "IDENTITY",
      "displayName": "ID verification",
      "description": "Verify a Kenyan national ID.",
      "category": "Identity",
      "status": "COMPLETED",
      "riskLevel": "LOW",
      "riskScore": 12,
      "summary": "Identity verified successfully",
      "flags": [],
      "details": {
        "matched": true,
        "idNumber": "28647415",
        "dateOfBirth": "1995-04-12"
      },
      "executedAt": "2026-09-19T18:42:10.944Z",
      "durationMs": 1280,
      "reused": false
    },
    "reportId": "7f000001-a0ae-1d9d-81a0-ae8e05830014",
    "checkId": "7f000001-a0ae-2222-81a0-ae8e05830014",
    "checkResultId": "7f000001-a0ae-2222-81a0-ae8e05830014",
    "checkType": "IDENTITY",
    "checkName": "IDENTITY",
    "checkDefinitionId": "7f000001-a077-1c27-81a0-774c84700000",
    "status": "COMPLETED",
    "riskLevel": "LOW",
    "riskScore": 12
  }
}

data carries the report the check belongs to, the subject, and the check result:

FieldDescription
data.workspaceIdYour workspace.
data.reportThe report the check belongs to — its id, status, paymentMode, consentStatus, and the checks it requested. Fetch the report for the full record.
data.subjectThe subject's id and type (INDIVIDUAL or COMPANY).
data.check.resultIdThis check result. Same value as data.check.id.
data.check.nameThe check name, as used in checksRequested.
data.check.displayNameHuman-readable name, safe to show in your own UI.
data.check.statusExecution status of the check.
data.check.riskLevelLOW, MEDIUM, HIGH or UNKNOWN.
data.check.riskScore0–100.
data.check.summaryOne-line outcome, written for people.
data.check.flagsAdverse findings raised by the check. Empty when there are none.
data.check.detailsThe provider result. Its fields depend on the check — see the check definition's outputFields.
data.check.executedAtWhen the check ran.
data.check.durationMsHow long the check took.
data.check.reusedtrue when an earlier, still-fresh result was reused instead of running the check again.

The flat fields at the end of datareportId, checkId, checkResultId, checkName, checkType, checkDefinitionId, status, riskLevel, riskScore — repeat values from data.report and data.check for convenience.

data.check.details can contain the subject's personal data — here an ID number and date of birth. Accept deliveries over HTTPS only, and treat stored payloads with the same care as the report itself.

Handling redelivery

A delivery that is not acknowledged is retried, so your endpoint can receive the same event more than once. Make your handler idempotent — for example, skip an event whose event and data.checkResultId you have already processed.

Verifying deliveries

Deliveries are signed with the secret you configured.

Confirm the signature header and algorithm with Verifisha before relying on verification in production.

Verify against the raw body bytes as received, before parsing. Re-serialising the JSON first will not match.

Testing and debugging

  • Send a test event to exercise your handler before real traffic arrives.
  • Delivery logs record each attempt with the HTTP status your endpoint returned and whether it is RETRYING. That is the first place to look when an event seems to have been missed.

Inbound payment webhooks are not yours to call

POST /api/v1/webhooks/paystack receives events from the payment provider, authenticated by the provider's own signature. It is unrelated to your outbound configuration and is not a customer API.

On this page