Adhere Web SDK
The Adhere Web SDK enables you to rapidly and securely verify user identities and perform facial liveness checks directly in your web applications. The SDK mounts a drop-in widget over your application, handling document capture, identity verification, and liveness detection — all in one seamless flow.Features
- Drop-in UI Modal — Responsive, animated widget that overlays your app via
SmartComplyFlow.open(). - CDN & npm Support — Install via npm/yarn or load directly from a CDN with zero build steps.
- Dynamic Routing — Automatically adapts document and verification requirements from your dashboard configuration.
- Single-Use Sessions —
clientIdis your permanent integration key (from your SDK Config) and is reused for every session. EachcreateSession()call issues a fresh, single-use session token — that token, not theclientId, is what’s scoped to one verification. - Multi-Country Identity — Supports data and document channels across Nigeria (BVN, NIN, passports, driver’s licenses, voter’s cards, and more), Ghana, Kenya, South Africa, and Cote d’Ivoire, with generic passport verification available for other countries and country-agnostic liveness/face-match checks — exact channels depend on what’s enabled in your dashboard.
- Two-Sided Document Capture — Front is always required; back is required, optional, or not offered depending on the document type (e.g. NIN is optional-back, since not every physical NIN document has a usable reverse side).
- Hardware Agnostic Liveness — Uses native webcam and MediaRecorder API for cross-platform compatibility. A single passive scan (blink + natural head movement) — no discrete step-by-step prompts.
Installation
Option 1 — CDN (No build step required, recommended)
Add the script tag to your HTML:window.SmartComplySDK:
Option 2 — npm / yarn
Unlike the CDN, npm has no auto-updating option — this is true for every npm package, not specific to ours.
npm install resolves to the latest version at the moment you run it, then locks that exact version in package-lock.json (or yarn.lock); it will not change again on its own. Run npm update smartcomply-web-sdk periodically (or before each deploy) to pick up new fixes — this stays within the ^1.0.x range already set in your package.json, and we commit to never shipping a breaking change within 1.x, so it’s always safe to run. Check npmjs.com/package/smartcomply-web-sdk for the current version number.Quick Start — Drop-in Widget (Recommended)
The easiest way to integrate is the drop-in widget. It handles the complete verification flow automatically.Get your API Key and Client ID from your Adhere Dashboard — both come from your SDK Config and are permanent; reuse the same values for every session. What’s single-use is the session token the SDK obtains internally via
createSession() (30-minute expiry, revoked after submission) — you never see or manage that token directly through the drop-in widget.The widget warns the user 2 minutes before this 30-minute expiry, then automatically shows a “session expired” message and closes itself (firing onClose) once the deadline actually passes — with a brief grace period if the user is mid-scan, so an active capture is never torn down mid-flow. If you need a fresh verification after that, call SmartComplyFlow.open() again to start a new session (available from smartcomply-web-sdk@1.0.76).Configuration Parameters
Environment URLs
Branding
The SDK Config on your Dashboard supplies the brand name, description and theme colour thatinitializeConfig() returns, and the widget renders them.
Framework Examples
React
Vue
Plain HTML (CDN)
Headless API (Advanced)
For full control over the UI, use theSmartComply class directly without the modal.
The liveness scan itself has its own 12-second capture window, separate from the 30-minute session TTL above. This timer starts only when active recording begins — right after the camera has centered on the user’s face and briefly re-stabilized (autofocus/exposure) — not from when the camera first opens. From that point, the widget shows a countdown ring around the face oval; the scan must detect the passive liveness signal (blink + natural head movement) within those 12 seconds or it fails with a “Time’s Up” screen and a Try Again button that restarts the scan using the same liveness entry (no re-charge).This timer is entirely local to the browser tab and only runs during the active scan — it is cleared the moment the scan resolves (success or timeout) and does not persist, run in the background, or resume if the user leaves and returns. In practice this means: if the user has already completed the liveness/face step and moved on to another part of the flow (e.g. reviewing their submission), the 12-second window is long finished and irrelevant — nothing about that step can time out again. The only clock still running at that point is the 30-minute session TTL, which governs the SDK’s API calls generally, not the completed scan.
onComplete Payload
onComplete fires as soon as the user finishes their part of the flow (the “Verification Submitted” screen renders) — it is a submission receipt, not a verification verdict. Backend processing (face match, document read, government DB check) continues after this fires, and status is always "processing" here regardless of the eventual outcome. The real pass/fail result only ever arrives via webhook.
verificationResult is only present for data verification (BVN/NIN) — it’s the immediate government-database lookup result confirming the ID number matched a real record. It says nothing about the face match, which is still pending. It’s absent for document verification flows.Receiving Results (Webhook)
The backend delivers exactly oneliveness.completed webhook per verification, to the URL configured in your SDK Config, once face matching (and OCR / government DB check, depending on the flow) has finished.
This is the SDK’s own webhook — configured per SDK Config and specific to
liveness.completed. It’s separate from the platform-wide webhook system described in Webhooks (transaction monitoring, general KYC module events, {success, module, event, data} shape). Both currently sign with HMAC-SHA256 and a sha256=-prefixed header, hex-encoded — verify against the raw request body either way.Payload shape
verification_type: "document_verification" and a document block (OCR fields + document-to-selfie face match) instead of customer_profile:
document also carries place_of_birth, place_of_issue, address, district, division, location, sub_location, serial_number, and barcode_number — null unless the specific document type carries that field (e.g. serial/barcode numbers mainly apply to newer Kenyan ID cards). face_match.reason is populated with a user-facing explanation when verified is false or the match was skipped.face_match.decision ("MATCH", "NO_MATCH", or "REJECTED") is what verified is actually derived from — read decision rather than comparing confidence_percentage against a threshold of your own, since our internal match threshold isn’t part of this payload and may change over time. "REJECTED" means liveness/anti-spoofing failed before any comparison ran, so confidence_percentage can be absent or 0 alongside verified: false for a reason unrelated to how similar the faces looked.verification_id matches the entryId your onComplete callback received.
Verify the signature
The signature header isX-Adhere-Signature: sha256=<hex> — note the sha256= prefix. It’s computed over the exact compact-JSON bytes of the request body, so your handler must verify against the raw body, not a re-serialized copy of the parsed JSON (re-stringifying can produce different bytes and the signature will never match).
Security Notes
- Client ID — Permanent, from your SDK Config. Reuse the same
clientIdfor every session — there’s no per-session ID to generate. - API Key — Never expose your API key in client-side code in production. Use environment variables.
- Session Tokens — The single-use part. Automatically obtained and managed by the SDK per verification via
createSession(), expire after 30 minutes, and are revoked immediately once liveness is submitted.

