Developer Guide

Webhook Integration

Ndini uses a webhook-based architecture. Simply redirect your users to our platform; once verification is complete, we'll push the results directly to your server.

1. Initiate Redirect

Point your "Verify Now" button to your unique token URL. This starts the automated verification flow in the user's browser.

URL https://ndini.openproject.co.zw/r/{TOKEN}
<!-- Frontend Link -->
<a href="https://ndini.openproject.co.zw/r/APP_TOKEN_HERE">
  Verify with Ndini
</a>

2. Receiving the Webhook

Set your Webhook URL in the Dashboard. Upon completion, Ndini sends a POST request to your endpoint.

Ensure your endpoint is public and returns a 200 OK status.

4. Pulse Check Requirement

Before Ndini creates a token, it validates your webhook by sending a POST request with event: "pulse". Your webhook must accept this event and return HTTP 200.

REQUEST POST /your-webhook-endpoint
{
  "event": "pulse",
  "timestamp": "2026-05-06T10:30:00Z"
}
EXPECTED RESPONSE HTTP 200
{
  "ok": true,
  "event": "pulse"
}

5. Webhook Payload Schemas

Webhook payloads are versioned and differ between v1 and v2.

v1 webhook payload

{
  "event": "verification.completed",
  "timestamp": "2026-05-26T10:30:00+00:00",
  "data": {
    "application": "Bank Account Opening",
    "id": "63-1234567 Z 32",
    "dob": "1990-01-01",
    "date_of_issue": "2015-05-20",
    "full_name": "JOHN DOE",
    "face_match": {
      "verified": false,
      "confidence": 42.37,
      "distance": 0.3921,
      "threshold": 0.4,
      "age_gape_passed": true,
      "age_gap_years": 8,
      "doc_age_years": 11,
      "dob_year": 1990
    },
    "hologram_check": {
      "passed": true
    },
    "liveness_phase1": {
      "passed": true
    }
  },
  "id": "63-1234567 Z 32"
}

v2 webhook payload

{
  "event": "verification.completed",
  "timestamp": "2026-05-26T10:30:00+00:00",
  "application": "Bank Account Opening",
  "attempt_id": "atm_12345",
  "id": "63-1234567 Z 32",
  "dob": "1990-01-01",
  "date_of_issue": "2015-05-20",
  "full_name": "JOHN DOE",
  "face_match": {
    "verified": false,
    "confidence": 42.37,
    "distance": 0.3921,
    "threshold": 0.4,
    "age_gape_passed": true,
    "age_gap_years": 8,
    "doc_age_years": 11,
    "dob_year": 1990
  },
  "face_decision": {
    "status": "manual_review",
    "verified": false,
    "confidence": 42.37,
    "verified_min": 50.0,
    "manual_min": 30.0
  },
  "hologram_check": {
    "passed": true
  },
  "liveness_phase1": {
    "passed": true,
    "reason": null,
    "reasons": [],
    "score": 0.92,
    "flags": [],
    "completed_challenges": 3,
    "total_seconds": 4.121
  },
  "liveness_image_path": "uploads/liveness/live_123.jpg"
}

Face Decision Policy

face decision status is policy based: verified at >=50%, manual review at 30%-49.99%, else fail.

ID Extraction

The webhook payload now includes comprehensive ID details like full_name, dob, and date_of_issue.