Verifide Liveness and Identity Comparism Embed (iframe)

Embed live face capture in your app via Verifide iframe + webhook.

Verifide Embed — Partner Integration Guide

Embed Verifide’s live face capture in your application via iframe. Verifide hosts the camera UI, calls Dikript with your credentials, and notifies your server when a check completes.

Two flows are available:

FlowPurpose
LivenessLive selfie only — confirms the user is present
Identity comparisonLive selfie matched against a government ID record — the end user picks NIN, BVN, or phone inside the iframe

Partner portal (UI): https://verifide.dikript.com/embed/partner
API BASE URL: https://api-verifide.dikript.com

After sign-in you get a dashboard with separate pages for each flow, settings, and session testing.

Portal routePurpose
/embed/partner/sign-inRegister or sign in with partner API key
/embed/partner/dashboardOverview and flow picker
/embed/partner/livenessCreate and preview liveness sessions
/embed/partner/identity-comparisonCreate and preview identity-match sessions
/embed/partner/settingsWebhook URL and Dikript key rotation

Overview

RoleWhat they do
Your backendRegisters credentials, creates sessions, receives webhooks
Your frontendEmbeds the session URL in an <iframe allow="camera">
End userTakes a live selfie inside the iframe — no Verifide login
VerifideCamera + face detection, Dikript call, webhook delivery
┌─────────────┐     register / create session      ┌──────────────┐
│ Your server │ ─────────────────────────────────► │ Verifide API │
└─────────────┘                                    └──────┬───────┘
       ▲                                                  │
       │ webhook (pass/fail)                              │ Dikript liveness
       │                                                  ▼
┌─────────────┐     iframe embed URL              ┌──────────────┐
│ Your app    │ ◄──────────────────────────────── │ Verifide UI  │
│ (browser)   │     user captures face            │ /embed/liveness/…
└─────────────┘                                   └──────────────┘

Credentials (three things — do not confuse them)

CredentialWho holds itPurpose
Dikript secret keyVerifide server (you register it once)Verifide calls Dikript liveness on your behalf
Partner API key (vf_embed_…)Your serverAuthenticate session creation and settings API
Webhook signing secretYour serverVerify that webhooks really came from Verifide

Never put the Dikript key or partner API key in your frontend, iframe URL, or mobile app.


Step 1 — Register (one time)

Option A: Partner portal

  1. Open /embed/partner (redirects to sign-in or dashboard)
  2. Register tab — fill in institution name, Dikript secret key, webhook URL
  3. Copy immediately (shown once): partner API key + webhook signing secret
  4. Use Sign in on return visits with your vf_embed_… key

Dashboard pages let you create test sessions and preview iframes. Production session creation should still run from your backend.

Option B: API

POST /api/v1/embed/partner/register
Content-Type: application/json

{
  "name": "Acme Bank",
  "dikriptSecretKey": "your-dikript-secret-key",
  "webhookUrl": "https://your-app.com/webhooks/verifide-liveness"
}

Response:

{
  "status": true,
  "data": {
    "partnerId": "…",
    "apiKey": "vf_embed_…",
    "webhookSecret": "…",
    "message": "Store the API key and webhook secret securely; they are shown once."
  }
}

Step 2 — Create a session

Flow 1: Liveness (each check)

Call from your backend when a user starts a flow that needs liveness (loan app, KYC step, etc.).

POST /api/v1/embed/liveness/sessions
X-Embed-Partner-Key: vf_embed_your_key_here
Content-Type: application/json

{
  "referenceId": "loan-application-8842"
}
FieldRequiredDescription
referenceIdNoYour internal ID — returned in the webhook so you can match the result

Response:

{
  "status": true,
  "data": {
    "sessionId": "…",
    "sessionToken": "…",
    "embedUrl": "https://verifide.dikript.com/embed/liveness/abc123…",
    "referenceId": "loan-application-8842",
    "status": "pending",
    "expiresAt": "2026-06-01T12:20:00Z"
  }
}

Give embedUrl to your frontend. Sessions expire in about 20 minutes and are single-use.

Flow 2: Identity comparison (each check)

Create a session from your backend. Only referenceId and optional allowedIdTypes are sent at creation time. The end user chooses ID type (unless restricted) and enters their number inside the embed wizard, then captures a selfie.

POST /api/v1/embed/identity-comparison/sessions
X-Embed-Partner-Key: vf_embed_your_key_here
Content-Type: application/json

{
  "referenceId": "loan-application-8842",
  "allowedIdTypes": ["BVN"]
}
FieldRequiredDescription
referenceIdNoYour internal ID — returned in the webhook
allowedIdTypesNoRestrict which ID types appear in the iframe. Values: NIN, BVN, PHONENUMBER. Example: ["BVN"] shows BVN only. Omit to allow all three.

If the user submits an ID type not in allowedIdTypes, the API rejects the capture.

Response:

{
  "status": true,
  "data": {
    "sessionId": "…",
    "sessionToken": "…",
    "embedUrl": "https://verifide.dikript.com/embed/identity-comparison/abc123…",
    "referenceId": "loan-application-8842",
    "allowedIdTypes": ["BVN"],
    "status": "pending",
    "expiresAt": "2026-06-01T12:20:00Z"
  }
}

After the user completes the wizard, your webhook receives the idType and idNumber they entered.


Step 3 — Embed in your app

Liveness iframe

<iframe
  src="https://verifide.dikript.com/embed/liveness/{sessionToken}"
  allow="camera"
  style="width:100%;min-height:520px;border:0"
></iframe>

Identity comparison iframe

<iframe
  src="https://verifide.dikript.com/embed/identity-comparison/{sessionToken}"
  allow="camera"
  style="width:100%;min-height:520px;border:0"
></iframe>

Use the full embedUrl from the create-session response — do not build the URL yourself unless you know the token.

What the user sees

Liveness

  1. Camera opens automatically (same live capture technology as Verifide verification)
  2. User centres face and captures
  3. Verifide submits to Dikript and shows success or failure
  4. Iframe notifies your page via postMessage and attempts to close

Identity comparison

  1. User selects ID type (NIN, BVN, or phone on ID record) — or only the types you set in allowedIdTypes
  2. User enters their ID number and continues to live selfie capture
  3. Verifide calls Dikript identity comparison and shows success or failure
  4. Iframe notifies your page via postMessage; webhook includes the ID the user entered

The iframe loads allowed ID types from GET …/sessions/{token}/public (field allowedIdTypes).

Listen for instant UI updates (optional)

LivenesspostMessage type verifide.liveness.complete:

window.addEventListener('message', (event) => {
  if (event.data?.type !== 'verifide.liveness.complete') return
  const { success, sessionToken, confidence, dikriptTransactionRef, message } = event.data
  // Update UI — still wait for webhook as source of truth
})

Identity comparisonpostMessage type verifide.identity_comparison.complete:

window.addEventListener('message', (event) => {
  if (event.data?.type !== 'verifide.identity_comparison.complete') return
  const { success, sessionToken, confidence, message } = event.data
})

Treat the webhook as the source of truth for your backend records. postMessage is for UX only.


Step 4 — Receive the webhook

When a session completes (pass or fail), Verifide POSTs JSON to your registered webhook URL.

Liveness — liveness.completed

Headers

HeaderValue
Content-Typeapplication/json
X-Verifide-Eventliveness.completed
X-Verifide-SignatureHMAC-SHA512 hex digest of the raw JSON body using your webhook signing secret

Body example

{
  "event": "liveness.completed",
  "sessionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "sessionToken": "abc123…",
  "referenceId": "loan-application-8842",
  "success": true,
  "confidence": 92,
  "dikriptTransactionRef": "N202606171805007061",
  "errorMessage": null,
  "completedAt": "2026-06-01T12:05:00Z",
  "captureImageUrl": "https://… (presigned S3 URL, ~1h TTL)",
  "captureImageBase64": "/9j/4AAQSkZJRg… (raw JPEG base64, no data-URI prefix)",
  "dikriptResponse": {
    "status": true,
    "message": "Successful",
    "code": "200",
    "transactionRef": "N202606171805007061",
    "data": { "…": "full Dikript liveness payload" }
  }
}
FieldDescription
captureImageUrlPresigned download URL for the selfie (~1 hour TTL when S3 is enabled)
captureImageBase64Same selfie as raw base64 JPEG — use when you cannot fetch the URL server-side
dikriptResponseFull passthrough of Dikript’s JSON response (not a summary). Parse data for provider-specific fields.

The selfie is also stored in Verifide object storage (S3 when enabled).

Identity comparison — identity_comparison.completed

Same signature verification as liveness. Header X-Verifide-Event: identity_comparison.completed.

{
  "event": "identity_comparison.completed",
  "sessionId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "sessionToken": "abc123…",
  "referenceId": "loan-application-8842",
  "idType": "BVN",
  "idNumber": "22270444036",
  "success": true,
  "confidence": 88,
  "dikriptTransactionRef": "N202606171805007061",
  "errorMessage": null,
  "completedAt": "2026-06-01T12:05:00Z",
  "captureImageUrl": "https://… (presigned S3 URL, ~1h TTL)",
  "captureImageBase64": "/9j/4AAQSkZJRg… (raw JPEG base64, no data-URI prefix)",
  "dikriptResponse": {
    "status": true,
    "message": "Successful",
    "code": "200",
    "apiVersion": "v1",
    "transactionRef": "N202606171805007061",
    "data": {
      "identity": {
        "type": "BVN",
        "number": "22270444036",
        "payload": {
          "bvn": "22270444036",
          "firstName": "OGONNAYA",
          "lastName": "AJAH",
          "dateOfBirth": "03-Mar-1991",
          "phoneNumber1": "07068085036"
        }
      },
      "comparison": {
        "isMatch": true,
        "confidence": 88.731,
        "detail": {
          "confidence": 88.731,
          "thresholds": { "_1e3": 62.327, "_1e4": 69.101, "_1e5": 73.975 }
        }
      }
    },
    "error": null
  }
}

Verifide sets top-level success and confidence (integer, 0–100) from Dikript’s envelope and data.comparison. The dikriptResponse object is the complete Dikript JSON — use it when you need identity payload fields, match detail, or thresholds.

Note: identity.payload.base64Image (when present) is the ID photo from the registry, not the live selfie. The live capture is captureImageUrl / captureImageBase64.

Verify signature (pseudocode)

Same pattern as Paystack/Dikript-style HMAC webhooks:

// C# example
var body = await reader.ReadToEndAsync();
var expected = HmacSha512Hex(body, webhookSigningSecret);
if (!FixedTimeEquals(expected, request.Headers["X-Verifide-Signature"]))
    return Unauthorized();
// Node.js example
const crypto = require('crypto')
const expected = crypto.createHmac('sha512', webhookSecret).update(rawBody).digest('hex')
if (expected !== req.headers['x-verifide-signature']) return res.sendStatus(401)

Use the raw request body — do not re-serialize parsed JSON before hashing.


Poll session status (optional)

If a webhook is delayed or fails, poll from your backend.

Liveness:

GET /api/v1/embed/liveness/sessions/{sessionToken}
X-Embed-Partner-Key: vf_embed_…   (optional — see below)

Identity comparison:

GET /api/v1/embed/identity-comparison/sessions/{sessionToken}
X-Embed-Partner-Key: vf_embed_…   (optional)

Auth: Pass X-Embed-Partner-Key when calling from your server (recommended). You may also poll with only the session token — the token acts as an opaque credential for that session.

Response fields (both flows, after completion):

FieldDescription
statuspending, completed, failed, or expired
successPass/fail when completed
confidenceInteger 0–100
dikriptTransactionRefDikript transaction reference
errorMessageSet when failed
completedAtUTC timestamp
captureImageUrlPresigned selfie URL (~1h TTL)
captureImageBase64Raw JPEG base64 (same selfie as URL)
dikriptResponseFull Dikript JSON passthrough

Identity comparison only (additional fields):

FieldDescription
allowedIdTypesID types permitted for this session (e.g. ["BVN"])
idType / idNumberMaskedID chosen by user (masked number on poll)
identityComparison{ "idType", "idNumber" } with full number when polling with partner key

Partner portal sign-in

  1. Open /embed/partner
  2. Sign in with your vf_embed_… partner API key, or Register if new
  3. Use Dashboard → Liveness or Identity match to create test sessions
  4. Settings to update webhook URL or rotate Dikript key

Production session creation should use the API from your server.


Update settings

PUT /api/v1/embed/partner/settings
X-Embed-Partner-Key: vf_embed_…
Content-Type: application/json

{
  "name": "Acme Bank",
  "webhookUrl": "https://your-app.com/webhooks/verifide-liveness",
  "dikriptSecretKey": "new-key-if-rotating"
}

Omit dikriptSecretKey to keep the existing key.


API reference summary

MethodPathAuthPurpose
POST/api/v1/embed/partner/registerNoneFirst-time registration
GET/api/v1/embed/partner/settingsX-Embed-Partner-KeyRead settings
PUT/api/v1/embed/partner/settingsX-Embed-Partner-KeyUpdate webhook / Dikript key
POST/api/v1/embed/liveness/sessionsX-Embed-Partner-KeyCreate liveness session
GET/api/v1/embed/liveness/sessions/{token}Partner key or session tokenPoll liveness status
GET/api/v1/embed/liveness/sessions/{token}/publicNoneIframe page only
POST/api/v1/embed/liveness/sessions/{token}/submitNoneIframe page only
POST/api/v1/embed/identity-comparison/sessionsX-Embed-Partner-KeyCreate identity session
GET/api/v1/embed/identity-comparison/sessions/{token}Partner key or session tokenPoll identity status
GET/api/v1/embed/identity-comparison/sessions/{token}/publicNoneIframe page only
POST/api/v1/embed/identity-comparison/sessions/{token}/submitNoneIframe page only

Base URL for API: your Verifide API host (e.g. https://api-verifide.dikript.com or your API subdomain).


Production checklist

  • Partner API key and webhook secret stored server-side only
  • Webhook endpoint verifies X-Verifide-Signature
  • Webhook handler is idempotent (same session may retry)
  • New session created per user attempt (do not reuse embed URLs)
  • Iframe includes allow="camera"
  • HTTPS on webhook URL
  • Frontend postMessage handler checks event.origin
  • Identity sessions use allowedIdTypes when you must limit NIN / BVN / phone choices
  • Webhook handler reads captureImageBase64 or fetches captureImageUrl before expiry
  • Backend stores or processes dikriptResponse if you need registry identity fields beyond pass/fail
  • Dikript key rotated via settings API if compromised

Troubleshooting

IssueLikely cause
“Invalid or missing partner API key”Wrong or missing X-Embed-Partner-Key header
“Session not found or expired”Token expired (~20 min) or already used
Webhook not receivedURL unreachable, TLS error, or non-2xx response — check server logs
Camera blockedIframe missing allow="camera" or user denied permission
Liveness failsPoor lighting, Dikript key invalid, or confidence below threshold
dikriptResponse looks like { "ValueKind": 1 }Upgrade to latest API — older builds serialized JSON incorrectly; redeploy backend
User sees ID type you did not expectCheck allowedIdTypes on session create; omit field to allow all three
captureImageBase64 is null on old sessionsOnly sessions completed after base64 storage was deployed include this field

Support

For credential rotation, webhook secret recovery, or production domain setup, contact your Verifide administrator.

Partner portal: https://verifide.dikript.com/embed/partner
Embed URLs:

  • Liveness: https://verifide.dikript.com/embed/liveness/{sessionToken}
  • Identity comparison: https://verifide.dikript.com/embed/identity-comparison/{sessionToken}