Integration Guide
Overview
Integrate v2client verification into your application using either:
- Modal/Iframe Integration - Embed verification in a modal or iframe
- Direct Integration - Redirect users to the verification portal
Modal/Iframe Integration
1. Include the SDK
<!-- Include the verification SDK -->
<script src="https://verify.stage.okid.io/verification-sdk.js"></script>
<!-- Include modal CSS -->
<link rel="stylesheet" href="https://verify.stage.okid.io/modal.css">2. Initialize the SDK
// Initialize the SDK
const verificationSDK = new VerificationSDK({
baseUrl: 'https://verify.stage.okid.io',
onSuccess: (data) => {
console.log('Verification completed:', data);
// Handle successful verification
// data.verification_id, data.status ('verified', 'needs_manual_review', etc.)
},
onError: (error) => {
console.error('Verification failed:', error);
// Handle verification error
},
onClose: () => {
console.log('Verification modal closed');
// Handle modal close
},
onRetryRequested: (data) => {
console.log('Retry requested:', data);
// CRITICAL: Generate new verification ID - failed ones cannot be reused
generateNewVerificationAndRetry();
}
});
async function generateNewVerificationAndRetry() {
// Call your server to generate a new verification ID with your API key
const response = await fetch('/api/generate-verification', { method: 'POST' });
const { verificationId } = await response.json();
// Start new verification with fresh ID
verificationSDK.openWithVerificationId(verificationId);
}3. Open Verification Modal
Option A: Direct Open (User generates verification)
// Let user generate their own verification
document.getElementById('verify-btn').addEventListener('click', () => {
verificationSDK.openDirect();
});Option B: Pre-generated Verification ID
// Use server-generated verification ID
async function startVerification() {
try {
// Generate verification ID on your server
const response = await fetch('/api/generate-verification', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
const { verificationId } = await response.json();
// Direct verification (skips selection screen)
verificationSDK.openWithVerificationId(verificationId);
// OR: Show selection screen (QR code + device choice)
// verificationSDK.openWithVerificationId(verificationId, true);
} catch (error) {
console.error('Failed to generate verification:', error);
}
}URL Patterns:
• Direct verification: /?verification_id=...
• Selection screen: /?verification_id=...&mode=select
Server-side Setup
Generate Verification ID (Required for Option B)
// Example: Node.js/Express endpoint
app.post('/api/generate-verification', async (req, res) => {
try {
const response = await fetch('https://verify.stage.okid.io/api/generate-verification', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-SDK-Key': process.env.OKID_API_KEY // Store API key securely on server
},
body: JSON.stringify({
// Optional: Attach custom metadata (returned in webhooks)
extra_data: {
reference_id: req.body.orderId,
user_id: req.user.id,
context: 'account_verification'
},
// Optional: Redirect user back to your app after verification
return_url: 'https://your-app.com/verification-complete'
})
});
if (!response.ok) {
throw new Error('Failed to generate verification');
}
const data = await response.json();
res.json({ verificationId: data.verificationId });
} catch (error) {
console.error('Verification generation failed:', error);
res.status(500).json({ error: 'Failed to generate verification' });
}
});Pro Tip: Use the extra_data field to attach custom metadata (order IDs, user references, etc.) to verifications. This data is stored with the verification and included in webhook notifications, making it easy to match verifications with your internal systems.
Direct Integration
For simpler integration, redirect users directly to the verification portal:
Direct Portal Access
// Let user generate verification themselves
window.location.href = 'https://verify.stage.okid.io/';
// Direct verification with pre-generated ID (skips selection)
window.location.href = 'https://verify.stage.okid.io/?verification_id=' + verificationId;
// Selection screen with pre-generated ID (shows QR + device choice)
window.location.href = 'https://verify.stage.okid.io/?verification_id=' + verificationId + '&mode=select';When to use mode=select:
• Desktop-first applications wanting mobile option
• When you want users to choose their verification device
• For QR code scanning experience with desktop animation
Verification Outcomes
The verification system will return one of these statuses:
verified- Identity successfully verifiedneeds_manual_review- Requires manual review by your teamrejected- Verification faileddocument_expired- Document has expiredWebView / Mobile App Integration
For mobile apps using WebView to display the verification flow, you can detect completion by monitoring URL changes.
URL-Based Completion Detection
When verification completes, the URL is updated with query parameters indicating the outcome. This eliminates the need for API polling.
Completion URL Pattern:https://verify.stage.okid.io/verify?verification_id=xxx&step=validation&status=complete&verification_status=verified
Query Parameters
status=complete- Indicates verification flow has finishedverification_status- The outcome: verified, rejected, needs_manual_review, document_expired, or errorImportant: The URL is updated using JavaScript history.replaceState(), which modifies the URL without triggering a page navigation. This means navigation-based callbacks like shouldOverrideUrlLoading (Android) or decidePolicyFor navigationAction (iOS) will not be triggered. Use the methods below to detect this change.
Android (WebView)
Use onPageFinished to check the URL after the page loads, or doUpdateVisitedHistory to detect replaceState changes:
webView.webViewClient = object : WebViewClient() {
// Method 1: Check URL when page finishes loading
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
url?.let { checkForCompletion(Uri.parse(it)) }
}
// Method 2: Detect replaceState/pushState changes (API 26+)
override fun doUpdateVisitedHistory(view: WebView?, url: String?, isReload: Boolean) {
super.doUpdateVisitedHistory(view, url, isReload)
url?.let { checkForCompletion(Uri.parse(it)) }
}
private fun checkForCompletion(url: Uri) {
if (url.getQueryParameter("status") == "complete") {
val verificationStatus = url.getQueryParameter("verification_status") ?: "unknown"
// Handle completion
handleVerificationComplete(verificationStatus)
// Close WebView activity
finish()
}
}
}iOS (WKWebView)
Use didFinish navigation delegate or KVO on the url property to detect URL changes:
// Method 1: Check URL when navigation finishes
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
checkForCompletion(webView.url)
}
// Method 2: Observe URL changes using KVO (catches replaceState)
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
if keyPath == "URL", let webView = object as? WKWebView {
checkForCompletion(webView.url)
}
}
// Don't forget to add observer in viewDidLoad:
// webView.addObserver(self, forKeyPath: "URL", options: .new, context: nil)
private func checkForCompletion(_ url: URL?) {
guard let url = url,
let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
components.queryItems?.first(where: { $0.name == "status" })?.value == "complete" else {
return
}
let verificationStatus = components.queryItems?
.first(where: { $0.name == "verification_status" })?.value ?? "unknown"
// Handle completion
handleVerificationComplete(status: verificationStatus)
// Close WebView
dismiss(animated: true)
}Advantage: URL monitoring eliminates the need for API polling, reducing server load and providing instant completion detection.
Retry Handling
When verification fails in SDK/modal context, the system sends a retry_requested message instead of redirecting to the portal.
Critical: Failed verification IDs cannot be reused. You must generate a new verification ID with your API key when handling retry requests.
Why Retry Handling is Needed
- Users coming from operator integrations used a custom API key to create the verification
- Simply redirecting to
/would lose the operator context - The operator must generate a new verification ID to maintain API key association
Implementation Example
// Complete retry handling implementation
const sdk = new VerificationSDK({
baseUrl: 'https://verify.stage.okid.io',
onSuccess: (data) => {
// Verification succeeded
handleVerificationSuccess(data);
},
onRetryRequested: (data) => {
console.log('Verification failed, retry requested:', data.reason);
// Show loading state to user
showRetryMessage('Generating new verification...');
// Generate new verification ID server-side
generateNewVerificationAndRetry();
}
});
async function generateNewVerificationAndRetry() {
try {
// Call your server endpoint that uses your API key securely
const response = await fetch('/api/generate-verification', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
if (!response.ok) {
throw new Error('Failed to generate new verification');
}
const { verificationId } = await response.json();
// Start new verification with fresh ID
sdk.openWithVerificationId(verificationId);
} catch (error) {
console.error('Retry failed:', error);
showErrorMessage('Unable to start new verification. Please try again.');
}
}
function showRetryMessage(message) {
// Show user-friendly message during retry
document.getElementById('status').textContent = message;
}
function showErrorMessage(message) {
// Show error if retry fails
document.getElementById('error').textContent = message;
}Troubleshooting
PostMessage events not triggering?
- Check browser console for iframe loading errors
- Verify the baseUrl is correct
- Ensure verification completes successfully
- Check for console logs starting with
[IFRAME]
Verification data not loading in iframe?
- Use
/?verification_id=...for direct verification - Use
/?verification_id=...&mode=selectonly if you want the selection screen - Ensure the verification_id was generated recently
- Check that your API key has the correct permissions
onRetryRequested not being called?
- Ensure you're using the latest version of verification-sdk.js
- Check browser console for
[SDK] Retry requestedmessages - Verify the callback is properly set in SDK initialization
- Make sure verification actually failed (not just closed by user)