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:
| Flow | Purpose |
|---|---|
| Liveness | Live selfie only — confirms the user is present |
| Identity comparison | Live 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 route | Purpose |
|---|---|
/embed/partner/sign-in | Register or sign in with partner API key |
/embed/partner/dashboard | Overview and flow picker |
/embed/partner/liveness | Create and preview liveness sessions |
/embed/partner/identity-comparison | Create and preview identity-match sessions |
/embed/partner/settings | Webhook URL and Dikript key rotation |
Overview
| Role | What they do |
|---|---|
| Your backend | Registers credentials, creates sessions, receives webhooks |
| Your frontend | Embeds the session URL in an <iframe allow="camera"> |
| End user | Takes a live selfie inside the iframe — no Verifide login |
| Verifide | Camera + 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)
| Credential | Who holds it | Purpose |
|---|---|---|
| Dikript secret key | Verifide server (you register it once) | Verifide calls Dikript liveness on your behalf |
Partner API key (vf_embed_…) | Your server | Authenticate session creation and settings API |
| Webhook signing secret | Your server | Verify 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
- Open
/embed/partner(redirects to sign-in or dashboard) - Register tab — fill in institution name, Dikript secret key, webhook URL
- Copy immediately (shown once): partner API key + webhook signing secret
- 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"
}
| Field | Required | Description |
|---|---|---|
referenceId | No | Your 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"]
}
| Field | Required | Description |
|---|---|---|
referenceId | No | Your internal ID — returned in the webhook |
allowedIdTypes | No | Restrict 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
- Camera opens automatically (same live capture technology as Verifide verification)
- User centres face and captures
- Verifide submits to Dikript and shows success or failure
- Iframe notifies your page via
postMessageand attempts to close
Identity comparison
- User selects ID type (NIN, BVN, or phone on ID record) — or only the types you set in
allowedIdTypes - User enters their ID number and continues to live selfie capture
- Verifide calls Dikript identity comparison and shows success or failure
- 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)
Liveness — postMessage 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 comparison — postMessage 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
liveness.completedHeaders
| Header | Value |
|---|---|
Content-Type | application/json |
X-Verifide-Event | liveness.completed |
X-Verifide-Signature | HMAC-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" }
}
}
| Field | Description |
|---|---|
captureImageUrl | Presigned download URL for the selfie (~1 hour TTL when S3 is enabled) |
captureImageBase64 | Same selfie as raw base64 JPEG — use when you cannot fetch the URL server-side |
dikriptResponse | Full 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
identity_comparison.completedSame 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 iscaptureImageUrl/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):
| Field | Description |
|---|---|
status | pending, completed, failed, or expired |
success | Pass/fail when completed |
confidence | Integer 0–100 |
dikriptTransactionRef | Dikript transaction reference |
errorMessage | Set when failed |
completedAt | UTC timestamp |
captureImageUrl | Presigned selfie URL (~1h TTL) |
captureImageBase64 | Raw JPEG base64 (same selfie as URL) |
dikriptResponse | Full Dikript JSON passthrough |
Identity comparison only (additional fields):
| Field | Description |
|---|---|
allowedIdTypes | ID types permitted for this session (e.g. ["BVN"]) |
idType / idNumberMasked | ID chosen by user (masked number on poll) |
identityComparison | { "idType", "idNumber" } with full number when polling with partner key |
Partner portal sign-in
- Open
/embed/partner - Sign in with your
vf_embed_…partner API key, or Register if new - Use Dashboard → Liveness or Identity match to create test sessions
- 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
| Method | Path | Auth | Purpose |
|---|---|---|---|
POST | /api/v1/embed/partner/register | None | First-time registration |
GET | /api/v1/embed/partner/settings | X-Embed-Partner-Key | Read settings |
PUT | /api/v1/embed/partner/settings | X-Embed-Partner-Key | Update webhook / Dikript key |
POST | /api/v1/embed/liveness/sessions | X-Embed-Partner-Key | Create liveness session |
GET | /api/v1/embed/liveness/sessions/{token} | Partner key or session token | Poll liveness status |
GET | /api/v1/embed/liveness/sessions/{token}/public | None | Iframe page only |
POST | /api/v1/embed/liveness/sessions/{token}/submit | None | Iframe page only |
POST | /api/v1/embed/identity-comparison/sessions | X-Embed-Partner-Key | Create identity session |
GET | /api/v1/embed/identity-comparison/sessions/{token} | Partner key or session token | Poll identity status |
GET | /api/v1/embed/identity-comparison/sessions/{token}/public | None | Iframe page only |
POST | /api/v1/embed/identity-comparison/sessions/{token}/submit | None | Iframe 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
postMessagehandler checksevent.origin - Identity sessions use
allowedIdTypeswhen you must limit NIN / BVN / phone choices - Webhook handler reads
captureImageBase64or fetchescaptureImageUrlbefore expiry - Backend stores or processes
dikriptResponseif you need registry identity fields beyond pass/fail - Dikript key rotated via settings API if compromised
Troubleshooting
| Issue | Likely 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 received | URL unreachable, TLS error, or non-2xx response — check server logs |
| Camera blocked | Iframe missing allow="camera" or user denied permission |
| Liveness fails | Poor 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 expect | Check allowedIdTypes on session create; omit field to allow all three |
captureImageBase64 is null on old sessions | Only 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}
