smartcomply-web-sdk
v1.0.19
Published
Drop-in identity verification (KYC) widget for web apps — face liveness, NIN/BVN, document verification
Maintainers
Readme
SmartComply Web SDK
Drop-in identity verification (KYC) widget for web applications. Embed one line of code and let your users verify their identity through facial liveness detection, government ID checks (BVN/NIN), and international document verification (passport, national ID, driver's license).
Overview
SmartComply SDK is a KYC-as-a-service widget that businesses embed in their web apps to verify end-user identities. It handles the full verification flow — from collecting ID details to live face matching — and delivers results asynchronously via webhook.
Target Audience
- Fintechs, banks, and lending platforms that need to onboard customers securely
- Any business required to perform Know Your Customer (KYC) checks before granting account access
What It Does
The SDK verifies that a person is real, alive, and who they claim to be:
- Identity verification — Validates the user's BVN/NIN against government databases, or extracts data from uploaded documents via OCR
- Liveness detection — Camera-based challenge-response (blink, turn head, open mouth) to prove the person is physically present — not a photo or video replay
- Face matching — Compares the user's live selfie against:
- The government-returned photo (BVN/NIN flow), or
- The face on their uploaded document (passport/license flow)
Two Verification Flows
| Flow | How it works | ID Types | |------|-------------|----------| | Data verification | User enters an ID number → backend validates against government database → face match against government photo | BVN, NIN | | Document verification | User uploads/captures a document photo → OCR extracts data + expiry check → face match against document photo | Passport, National ID, Driver's License |
End-to-End Flow
┌─────────────────────────────────────────────────────────────────────┐
│ YOUR APP │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ SmartComply Widget (modal) │ │
│ │ │ │
│ │ Welcome → Country → ID Type → Enter ID / Upload Doc │ │
│ │ → Face Liveness (camera) → Done │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │ │
│ submit │ │
│ ▼ │
│ Adhere Backend │
│ │ │
│ ┌─────────┼──────────┐ │
│ ▼ ▼ ▼ │
│ Face Match OCR Gov DB Check │
│ (DeepFace) (document) (BVN/NIN) │
│ └─────────┼──────────┘ │
│ ▼ │
│ Webhook POST ──────────► YOUR SERVER │
│ (signed with HMAC) (result handler) │
└─────────────────────────────────────────────────────────────────────┘Install
npm install smartcomply-web-sdkAlternative: CDN (No Build Step)
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/smartcomply.browser.js"></script>
<script>
// SDK available as window.SmartComplySDK
SmartComplySDK.SmartComplyFlow.open({
apiKey: "your_api_key",
clientId: "your_client_id",
onComplete: (result) => console.log("Done:", result),
});
</script>Option 1: Drop-in Widget (Recommended)
The fastest way to add KYC. Opens a complete, branded verification modal — handles everything automatically.
<button id="verify-btn">Verify Identity</button>
<script type="module">
import { SmartComplyFlow } from "smartcomply-web-sdk";
document.getElementById("verify-btn").onclick = () => {
SmartComplyFlow.open({
apiKey: "pk_live_your_api_key",
clientId: "your-sdk-config-uuid",
environment: "production",
onComplete(result) {
// Verification submitted! Final result arrives via webhook.
console.log("Entry ID:", result.entryId);
console.log("Status:", result.status); // "processing"
// Update your UI — tell the user to wait for confirmation
},
onError(err) {
console.error("Verification failed:", err.message);
},
onClose() {
// User closed the modal without completing
},
});
};
</script>That's it. The SDK will:
- Create a secure session with the Adhere backend
- Show a branded welcome screen (your brand name + theme from dashboard)
- Let the user select their country and ID type
- Collect their BVN/NIN number or capture their document photo
- Run face liveness detection (camera + challenge actions)
- Record a video and submit everything to the backend
- Show a result screen and call your
onCompletecallback
What the User Sees
| Step | Screen | Description | |------|--------|-------------| | 1 | Welcome | Your brand name, description, and what to expect | | 2 | Country | Select country (auto-skipped if only one country configured) | | 3 | ID Type | Choose from the channels you configured (e.g., BVN, NIN, Passport) | | 4a | ID Input | Enter BVN/NIN number → instant backend verification against government DB | | 4b | Document | Capture a photo of passport/ID card (camera or file upload) | | 5 | Liveness | Camera opens — user completes challenge actions (blink, turn head, open mouth) | | 6 | Done | "Verification Submitted" — user clicks Done, result arrives via webhook |
SmartComplyFlow.open() Options
SmartComplyFlow.open({
// Required
apiKey: string, // Your API key from the Adhere dashboard
clientId: string, // UUID from your SDK Config (created in dashboard)
// Optional
environment: "production" | "sandbox", // Default: "sandbox"
timeout: number, // Request timeout in ms (default: 30000)
// Callbacks
onComplete: (result) => void, // Verification submitted successfully
onError: (error) => void, // Unrecoverable error
onClose: () => void, // User closed the modal
});onComplete Result
{
entryId: 42, // Use this to track the verification
status: "processing", // Always "processing" — final result via webhook
submittedAt: "2026-04-13T...", // ISO timestamp
verificationResult: { // Only for data verification (BVN/NIN)
status: "success",
code: "VERIFICATION_COMPLETE",
data: { first_name: "Amara", last_name: "Okafor", identity_check_id: 123, ... }
}
}Option 2: Headless (Custom UI)
For full control over the user interface, use the SDK's API methods directly.
Initialize
import { SmartComply } from "smartcomply-web-sdk";
const sdk = new SmartComply({
apiKey: "pk_live_your_api_key",
clientId: "your-sdk-config-uuid",
environment: "production",
});Create Session
Every verification flow starts with a session. Sessions last 30 minutes and are single-use (revoked after liveness submission).
const session = await sdk.createSession();
// session.token — used internally for all subsequent API calls
// session.expires_at — ISO timestampLoad Configuration
Fetch your SDK config (brand name, theme, available ID types per country):
const config = await sdk.initializeConfig();
console.log(config.brand_name); // "Your Company Name"
console.log(config.verification_type); // ["data_verification"]
// Channels are grouped by country with field definitions:
console.log(config.channels);
// {
// "nigeria": [
// { id: 8, name: "National Identity Number (NIN)", fields: [{ type: "input", label: "Identification Number" }] },
// { id: 2, name: "Bank Verification Number Advanced (BVN)", fields: [{ type: "input", label: "Bank Verification Number" }] }
// ]
// }Use config.channels to build your own country/ID type selector. Each channel's fields array tells you what inputs to render.
Verify Identity (Data Verification)
For BVN/NIN — validates against the government database:
const result = await sdk.onboarding.verify({
identity_type_id: 8, // Channel ID from config.channels
fields: { identification_number: "12345678901" }, // Field keys from channel's fields definition
});
if (result.status === "success") {
console.log(result.data.identity_check_id); // Save this — needed for face matching in liveness
console.log(result.data.first_name); // "Amara"
}Liveness Check (with built-in camera UI)
Mount the SDK's liveness UI into any container element:
const container = document.getElementById("liveness-container");
const result = await sdk.liveness.startCheck(container, {
identifier: "12345678901", // The ID number entered by the user
identifier_type: "NIN",
country: "NG",
id_file: documentBlob, // Optional: document photo (for document flow)
identity_check: identityCheckId, // Optional: from verify response (for data flow)
}, ["BLINK", "TURN_LEFT", "OPEN_MOUTH"]);
console.log(result.status); // "processing"The SDK handles camera access, face detection, action prompts, video recording, and submission.
Liveness Check (fully manual)
If you want to handle camera and recording yourself:
// 1. Create entry
const entry = await sdk.liveness.create({
identifier: "A12345678",
identifier_type: "Passport",
country: "US",
challenge_actions: ["BLINK", "TURN_LEFT", "OPEN_MOUTH"],
autoshot_file: selfieBlob, // Captured selfie (JPEG/PNG, max 5MB)
id_file: passportPhotoBlob, // Optional: document photo
identity_check: identityCheckId, // Optional: from verify response
});
// 2. Run your own camera/detection/recording UI
// ...
// 3. Submit the recorded video + snapshot
const result = await sdk.liveness.submit(entry.id, videoBlob, snapshotBlob);
// result.status === "processing"After submission, the session is revoked. Each clientId can only complete one full verification.
Receiving Results (Webhook)
Verification is processed asynchronously. After the user submits, the backend runs face matching using AI (DeepFace with RetinaFace detection) and delivers results via webhook to the URL configured in your SDK Config.
Webhook Payload — Data Verification (BVN/NIN)
POST https://your-server.com/webhook
Content-Type: application/json
X-Adhere-Signature: <hmac-sha256-hex>
{
"event": "liveness.completed",
"data": {
"entry_id": 42,
"status": "passed",
"is_verified": true,
"match_score": 0.15,
"confidence_percentage": 62.5,
"identity_face_match": {
"verified": true,
"distance": 0.12,
"threshold": 0.4,
"confidence_percentage": 70.0
},
"metadata": { "ip_address": "...", "user_agent": "..." }
}
}Webhook Payload — Document Verification (Passport/License)
{
"event": "liveness.completed",
"data": {
"entry_id": 43,
"status": "passed",
"is_verified": true,
"match_score": 0.18,
"confidence_percentage": 55.0,
"document_verification": {
"status": "verified",
"document_type": "passport",
"is_expired": false,
"face_match_verified": true,
"face_match_score": 0.22,
"extracted_name": "AMARA OKAFOR",
"extracted_expiry_date": "2030-06-15"
},
"metadata": { "ip_address": "...", "user_agent": "..." }
}
}Understanding the Scores
match_score(distance) — Raw distance between face embeddings. Lower = more similar. Threshold is 0.4 (Facenet default).confidence_percentage— Human-readable score: how close the match is to perfect. 100% = identical, 0% = at the threshold boundary.identity_face_match— Only present in data verification (BVN/NIN). Confirms the person doing liveness matches the face on the government database.document_verification— Only present in document verification. Includes OCR results and document-to-selfie face match.
Possible Statuses
| Status | Meaning |
|--------|---------|
| passed | All checks passed — person is verified |
| failed | Face mismatch, expired document, or processing error |
| processing | Still being processed (rare — usually completes in seconds) |
Verify Signature
Always verify the webhook signature to prevent spoofing:
const crypto = require("crypto");
app.post("/webhook/smartcomply", express.json(), (req, res) => {
const signature = req.headers["x-adhere-signature"];
const secret = process.env.WEBHOOK_SECRET.replace(/-/g, "");
const expected = crypto
.createHmac("sha256", secret)
.update(JSON.stringify(req.body))
.digest("hex");
const isValid = crypto.timingSafeEqual(
Buffer.from(signature, "hex"),
Buffer.from(expected, "hex")
);
if (!isValid) return res.status(401).send("Bad signature");
// Handle the event
const { event, data } = req.body;
if (event === "liveness.completed" && data.is_verified) {
// ✓ User is verified — update your database
markUserAsVerified(data.entry_id);
}
res.json({ received: true });
});Challenge Actions
| Action | What the user does |
|--------|-------------------|
| BLINK | Blink both eyes |
| TURN_LEFT | Turn head to the left |
| TURN_RIGHT | Turn head to the right |
| TURN_HEAD | Turn head in any direction |
| OPEN_MOUTH | Open mouth wide |
Actions must be UPPERCASE. We recommend using 3 actions: ["BLINK", "TURN_LEFT", "OPEN_MOUTH"].
Error Handling
import { SDKError, AuthError, NetworkError } from "smartcomply-web-sdk";
try {
await sdk.createSession();
} catch (err) {
if (err instanceof AuthError) {
// 401: Invalid API key, expired session, or disabled branch
} else if (err instanceof NetworkError) {
// No internet, timeout, DNS failure
} else if (err instanceof SDKError) {
// Backend error: validation, insufficient balance, etc.
console.log(err.statusCode); // HTTP status
console.log(err.errorCode); // Machine-readable code
console.log(err.errorData); // Additional error details
}
}| Error Code | HTTP | Meaning |
|------------|------|---------|
| INVALID_API_KEY | 401 | Bad API key or expired session |
| SDK_CONFIG_NOT_FOUND | 404 | Invalid clientId |
| VALIDATION_ERROR | 400 | Missing or invalid fields |
| INSUFFICIENT_BALANCE | 402 | Top up your wallet |
Theming
The widget automatically uses the theme from your SDK Config (set in the Adhere dashboard):
default— Clean blue on whitemidnight_blue— Dark mode with blue accentssunset_gold— Warm gold on creamforest_emerald— Green on mint
No client-side theme configuration required.
Browser Support
Requires camera access (getUserMedia), video recording (MediaRecorder), and WebAssembly.
| Browser | Version | |---------|---------| | Chrome | 80+ | | Firefox | 75+ | | Safari | 14+ | | Edge | 80+ |
Prerequisites
- Adhere account — Sign up at the dashboard
- API Key — Format:
pk_live_...(from dashboard → API Keys) - SDK Config — Create in dashboard with your brand name, theme, verification types, channels, and webhook URL
- Client ID — The UUID shown on your SDK Config
- Funded wallet — Each verification deducts from your balance
- Webhook endpoint — A URL on your server to receive verification results
TypeScript Support
Full TypeScript definitions are included. Key types:
import type {
FlowOptions,
FlowResult,
SDKConfig,
SDKInitConfig,
SessionResponse,
VerifyIdentityResponse,
LivenessCreateResponse,
LivenessSubmitResponse,
ChallengeAction,
LivenessWebhookPayload,
ApiResponse,
} from "smartcomply-web-sdk";Quick Start (Test in 2 Minutes)
For production, always use npm install. Clone only for testing/contributing.
# 1. Install globally (for testing)
npm install -g smartcomply-web-sdk
# 2. Or clone and run demo (testing only)
git clone https://github.com/386konsult/smartcomply-web-sdk.git
cd smartcomply-web-sdk
npm install
npm run demo
# Opens http://localhost:3000 with test pageTesting
Test the SDK locally before integrating:
cd smartcomply-web-sdk
npm run demoThis starts a mock API server and opens a browser test page. Use these test credentials:
- API Key: Any string works in demo mode
- Client ID: Create one in your Adhere dashboard
License
ISC
