Mobile & Flutter Integration
Integrate YeboVerify into your mobile apps for seamless KYC verification.
Overview
YeboVerify uses a web-based verification flow that works with any mobile platform:
- Your app calls our API to create a verification session
- Open the verification URL in a browser/WebView
- User completes verification (ID + selfie)
- Webhook notifies your backend of the result
- User returns to your app
Quick Start
1. Create Session (Backend)
// Node.js backend
const response = await fetch('https://yeboverify-api.run.app/v1/sessions/create', {
method: 'POST',
headers: {
'X-API-Key': 'yv_live_YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
externalRef: 'user_123', // Your user ID
redirectUrl: 'yourapp://verify', // Deep link back to app
}),
});
const { verifyUrl, sessionToken } = await response.json();
// verifyUrl: https://verify.yeboverify.com?session=xxx2. Open Verification (Mobile)
// Flutter
import 'package:url_launcher/url_launcher.dart';
await launchUrl(
Uri.parse(verifyUrl),
mode: LaunchMode.externalApplication,
);// iOS
UIApplication.shared.open(URL(string: verifyUrl)!)// Android
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(verifyUrl))
startActivity(intent)3. Handle Return (Deep Link)
// Flutter - Handle deep link
// In your app's route handler:
if (uri.path == '/verify') {
final status = uri.queryParameters['status'];
if (status == 'success') {
// Verification completed - fetch updated user
await refreshUser();
}
}4. Receive Webhook (Backend)
// Your webhook endpoint
app.post('/webhooks/yeboverify', (req, res) => {
const { event, externalRef, decision, extractedData } = req.body;
if (event === 'verification.completed') {
if (decision === 'approved') {
// Update user as verified
await updateUser(externalRef, {
kycVerified: true,
kycName: `${extractedData.names} ${extractedData.surname}`,
kycCountry: extractedData.issuingCountry,
});
}
}
res.json({ received: true });
});Flutter Widget Library
For Flutter apps, we provide pre-built widgets that handle the entire flow.
Installation
# pubspec.yaml
dependencies:
yeboverify_flutter:
git:
url: https://github.com/omegathesecond/yeboverify-flutter.gitYeboVerifyButton
One-tap verification button:
YeboVerifyButton(
apiKey: 'yv_live_YOUR_API_KEY',
externalRef: currentUser.id,
onSuccess: (VerificationResult result) {
print('Verified: ${result.extractedData.names}');
refreshUser();
},
onError: (String error) {
showSnackbar(error);
},
// Customization
text: 'Verify Identity',
style: YeboVerifyButtonStyle.filled,
)YeboVerifyBadge
Display verification status:
YeboVerifyBadge(
isVerified: user.kycVerified,
size: 24,
)YeboVerifyCard
Full verification card with status and action:
YeboVerifyCard(
isVerified: user.kycVerified,
verifiedAt: user.kycVerifiedAt,
country: user.kycCountry,
onVerify: () async {
final session = await createSession(user.id);
launchUrl(Uri.parse(session.verifyUrl));
},
)React Native
Create Session
const startVerification = async (userId) => {
const response = await fetch(`${API_URL}/kyc/start`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${userToken}`,
},
});
const { verifyUrl } = await response.json();
// Open in browser
await Linking.openURL(verifyUrl);
};Handle Deep Link
// App.js
useEffect(() => {
const handleDeepLink = ({ url }) => {
if (url.includes('/verify')) {
const params = new URLSearchParams(url.split('?')[1]);
if (params.get('status') === 'success') {
refreshUser();
}
}
};
Linking.addEventListener('url', handleDeepLink);
return () => Linking.removeEventListener('url', handleDeepLink);
}, []);iOS (Swift)
Start Verification
func startVerification() async throws {
let url = URL(string: "\(apiUrl)/v1/sessions/create")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue(apiKey, forHTTPHeaderField: "X-API-Key")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body = ["externalRef": userId, "redirectUrl": "myapp://verify"]
request.httpBody = try JSONEncoder().encode(body)
let (data, _) = try await URLSession.shared.data(for: request)
let result = try JSONDecoder().decode(SessionResponse.self, from: data)
await UIApplication.shared.open(URL(string: result.verifyUrl)!)
}Handle Return
// SceneDelegate.swift
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let url = URLContexts.first?.url else { return }
if url.path == "/verify" {
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
let status = components?.queryItems?.first(where: { $0.name == "status" })?.value
if status == "success" {
NotificationCenter.default.post(name: .verificationComplete, object: nil)
}
}
}Android (Kotlin)
Start Verification
suspend fun startVerification(userId: String) {
val response = apiService.createSession(
CreateSessionRequest(
externalRef = userId,
redirectUrl = "myapp://verify"
)
)
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(response.verifyUrl))
startActivity(intent)
}Handle Return
// In your Activity
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
intent?.data?.let { uri ->
if (uri.path == "/verify") {
val status = uri.getQueryParameter("status")
if (status == "success") {
viewModel.refreshUser()
}
}
}
}AndroidManifest.xml
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" android:host="verify" />
</intent-filter>
</activity>Session Options
const session = await createSession({
externalRef: 'user_123', // Required: Your user ID
// Optional
redirectUrl: 'myapp://verify', // Deep link after verification
webhookUrl: 'https://api.you.com/webhook', // Override default webhook
metadata: { // Custom data (returned in webhook)
plan: 'premium',
source: 'mobile_app'
},
// UX options
language: 'en', // UI language
theme: 'dark', // dark or light
});Webhook Payload
{
"event": "verification.completed",
"verificationId": "ver_abc123",
"externalRef": "user_123",
"decision": "approved",
"confidence": "high",
"faceScore": 99.72,
"ocrConfidence": 95.5,
"extractedData": {
"surname": "DLAMINI",
"names": "SIPHO DAVID",
"dateOfBirth": "1990-05-15",
"sex": "Male",
"idNumber": "9005155555083",
"nationality": "Swazi",
"issuingCountry": "Eswatini",
"documentType": "National ID",
"issueDate": "2020-01-15",
"expiryDate": "2030-01-15"
},
"timestamp": "2026-03-27T01:00:00Z"
}Best Practices
1. Always Use Backend Sessions
Never expose your API key in mobile apps. Create sessions from your backend.
// ❌ Bad - API key in app
final session = await YeboVerify.createSession(apiKey: 'yv_live_xxx');
// ✅ Good - Call your backend
final session = await yourApi.startKyc(userId);2. Handle All States
switch (verificationStatus) {
case 'success':
showSuccess('Verification complete!');
break;
case 'failed':
showError('Verification failed. Please try again.');
break;
case 'cancelled':
// User closed without completing
break;
case 'expired':
showError('Session expired. Please try again.');
break;
}3. Implement Webhooks
Don't rely only on redirect status. Always verify via webhook.
// Your backend
app.post('/webhooks/yeboverify', async (req, res) => {
// Verify the replay-resistant V2 signature (X-YeboVerify-Signature-V2 +
// X-YeboVerify-Timestamp) — see /webhooks for the full verifyWebhookSignatureV2
// implementation. Do not verify against the legacy X-YeboVerify-Signature
// alone; it has no replay protection.
const timestamp = req.headers['x-yeboverify-timestamp'];
const signatureV2 = req.headers['x-yeboverify-signature-v2'];
if (!verifyWebhookSignatureV2(req.body, timestamp, signatureV2, WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process webhook
const { event, externalRef, decision } = req.body;
// ...
});4. Show Progress
YeboVerifyButton(
onPressed: () async {
setState(() => isLoading = true);
try {
final session = await api.startKyc(userId);
await launchUrl(Uri.parse(session.verifyUrl));
} finally {
setState(() => isLoading = false);
}
},
child: isLoading
? CircularProgressIndicator()
: Text('Verify Identity'),
)Business (KYB) Verification from Mobile
If your app has a business-onboarding flow (e.g. a merchant/vendor signing up), the same launchUrl / hosted-browser pattern above applies — just create the verification with POST /v1/company-verifications on your backend instead of /v1/sessions/create, and send the authorized signatory to the returned verifyUrl. They complete the identical ID + selfie capture; your backend uploads the company's own documents (registration certificate, proof of address, etc.) separately via the API. See Company Verification (KYB) for the full flow.
Support
- API Reference: /api-reference
- Webhooks: /webhooks
- Company Verification (KYB): /kyb
- Error Codes: /error-codes
- Email: [email protected]