Integration Guide
Complete guide for integrating VerifyHuman into your application with code examples in multiple languages.
Integration Overview
VerifyHuman integration follows a simple 3-step flow:
- User uploads a selfie → Frontend sends to VerifyHuman
- VerifyHuman verifies → Returns result with JWT token
- Your backend validates → Uses token to confirm verification
┌─────────┐ ┌──────────────┐ ┌─────────────┐
│ User │────1───>│ Frontend │────2───>│ VerifyHuman │
│ (Selfie)│ │ (Your App) │<───3────│ API │
└─────────┘ └──────┬───────┘ └─────────────┘
│
4 (Validate Token)
│
▼
┌──────────────┐
│ Backend │
│ (Your API) │
└──────────────┘
Quick Start Examples
JavaScript / React
import React, { useState } from 'react';
function VerificationComponent() {
const [status, setStatus] = useState('');
const [loading, setLoading] = useState(false);
const handleVerification = async (event) => {
const file = event.target.files[0];
if (!file) return;
setLoading(true);
const formData = new FormData();
formData.append('clientKey', process.env.REACT_APP_VERIFYHUMAN_KEY);
formData.append('file', file);
try {
const response = await fetch('https://app.verifyhuman.io/api/verify', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.status === 'PASS') {
setStatus(`✓ Verified! (${result.confidence}% confidence)`);
// Send token to your backend for validation
await validateOnBackend(result.token);
} else {
setStatus(`✗ Verification failed: ${result.error}`);
}
} catch (error) {
setStatus(`Error: ${error.message}`);
} finally {
setLoading(false);
}
};
const validateOnBackend = async (token) => {
await fetch('/api/verify-user', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ verificationToken: token })
});
};
return (
<div>
<h2>Human Verification</h2>
<input
type="file"
accept="image/*"
capture="user"
onChange={handleVerification}
disabled={loading}
/>
{loading && <p>Verifying...</p>}
{status && <p>{status}</p>}
</div>
);
}
export default VerificationComponent;
Backend validation (Node.js/Express):
const express = require('express');
const fetch = require('node-fetch');
app.post('/api/verify-user', async (req, res) => {
const { verificationToken } = req.body;
try {
const response = await fetch('https://app.verifyhuman.io/api/validate', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.VERIFYHUMAN_SECRET}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ token: verificationToken })
});
const result = await response.json();
if (result.valid && result.status === 'PASS') {
// Mark user as verified in your database
await db.users.update({
id: req.user.id,
verified: true,
verified_at: new Date()
});
res.json({ success: true, message: 'User verified!' });
} else {
res.status(401).json({ success: false, message: 'Invalid verification' });
}
} catch (error) {
res.status(500).json({ error: error.message });
}
});
Python / Flask
from flask import Flask, request, jsonify
import requests
import os
app = Flask(__name__)
@app.route('/verify', methods=['POST'])
def verify_user():
"""Frontend endpoint to submit verification"""
if 'file' not in request.files:
return jsonify({'error': 'No file uploaded'}), 400
file = request.files['file']
# Send to VerifyHuman API
files = {'file': (file.filename, file.stream, file.content_type)}
data = {'clientKey': os.environ.get('VERIFYHUMAN_CLIENT_KEY')}
response = requests.post(
'https://app.verifyhuman.io/api/verify',
files=files,
data=data
)
result = response.json()
return jsonify(result)
@app.route('/validate-verification', methods=['POST'])
def validate_verification():
"""Backend endpoint to validate verification token"""
token = request.json.get('token')
if not token:
return jsonify({'error': 'Missing token'}), 400
# Validate with VerifyHuman API
response = requests.post(
'https://app.verifyhuman.io/api/validate',
headers={
'Authorization': f"Bearer {os.environ.get('VERIFYHUMAN_SECRET')}",
'Content-Type': 'application/json'
},
json={'token': token}
)
result = response.json()
if result.get('valid') and result.get('status') == 'PASS':
# Mark user as verified
# db.update_user(user_id, verified=True)
return jsonify({'success': True, 'message': 'User verified'})
else:
return jsonify({'success': False, 'error': result.get('error')}), 401
if __name__ == '__main__':
app.run(port=5000)
Python / Django
# views.py
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.conf import settings
import requests
@csrf_exempt
def verify_user(request):
"""Handle file upload and verification"""
if request.method != 'POST':
return JsonResponse({'error': 'POST required'}, status=405)
if 'file' not in request.FILES:
return JsonResponse({'error': 'No file uploaded'}, status=400)
file = request.FILES['file']
# Send to VerifyHuman
files = {'file': file}
data = {'clientKey': settings.VERIFYHUMAN_CLIENT_KEY}
response = requests.post(
'https://app.verifyhuman.io/api/verify',
files=files,
data=data
)
return JsonResponse(response.json())
@csrf_exempt
def validate_token(request):
"""Validate verification token on backend"""
import json
data = json.loads(request.body)
token = data.get('token')
response = requests.post(
'https://app.verifyhuman.io/api/validate',
headers={
'Authorization': f'Bearer {settings.VERIFYHUMAN_SECRET}',
'Content-Type': 'application/json'
},
json={'token': token}
)
result = response.json()
if result.get('valid') and result.get('status') == 'PASS':
# Update user model
if request.user.is_authenticated:
request.user.is_verified = True
request.user.save()
return JsonResponse({'success': True})
return JsonResponse({'success': False}, status=401)
PHP / Laravel
<?php
// routes/web.php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
Route::post('/verify', function (Request $request) {
$request->validate([
'file' => 'required|image|max:5120' // 5MB max
]);
// Send to VerifyHuman API
$response = Http::attach(
'file',
file_get_contents($request->file('file')->path()),
$request->file('file')->getClientOriginalName()
)->post('https://app.verifyhuman.io/api/verify', [
'clientKey' => env('VERIFYHUMAN_CLIENT_KEY')
]);
return response()->json($response->json());
});
Route::post('/validate-verification', function (Request $request) {
$token = $request->input('token');
// Validate with VerifyHuman
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . env('VERIFYHUMAN_SECRET')
])->post('https://app.verifyhuman.io/api/validate', [
'token' => $token
]);
$result = $response->json();
if ($result['valid'] && $result['status'] === 'PASS') {
// Mark user as verified
$user = auth()->user();
$user->verified = true;
$user->save();
return response()->json(['success' => true]);
}
return response()->json(['success' => false], 401);
});
Ruby / Rails
# app/controllers/verification_controller.rb
class VerificationController < ApplicationController
def verify
require 'net/http'
require 'uri'
file = params[:file]
uri = URI.parse('https://app.verifyhuman.io/api/verify')
request = Net::HTTP::Post.new(uri)
form_data = [
['clientKey', ENV['VERIFYHUMAN_CLIENT_KEY']],
['file', file]
]
request.set_form form_data, 'multipart/form-data'
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
render json: JSON.parse(response.body)
end
def validate_token
require 'net/http'
require 'uri'
require 'json'
token = params[:token]
uri = URI.parse('https://app.verifyhuman.io/api/validate')
request = Net::HTTP::Post.new(uri)
request['Authorization'] = "Bearer #{ENV['VERIFYHUMAN_SECRET']}"
request['Content-Type'] = 'application/json'
request.body = { token: token }.to_json
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
result = JSON.parse(response.body)
if result['valid'] && result['status'] == 'PASS'
current_user.update(verified: true)
render json: { success: true }
else
render json: { success: false }, status: :unauthorized
end
end
end
Mobile App Integration
React Native
import React, { useState } from 'react';
import { View, Button, Text, Image } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
export default function VerificationScreen() {
const [status, setStatus] = useState('');
const pickImageAndVerify = async () => {
// Request camera permission
const permission = await ImagePicker.requestCameraPermissionsAsync();
if (!permission.granted) {
alert('Camera permission required');
return;
}
// Take photo
const result = await ImagePicker.launchCameraAsync({
allowsEditing: false,
quality: 0.8,
});
if (!result.canceled) {
await verifyImage(result.assets[0].uri);
}
};
const verifyImage = async (imageUri) => {
const formData = new FormData();
formData.append('clientKey', 'pk_live_your_key_here');
formData.append('file', {
uri: imageUri,
type: 'image/jpeg',
name: 'selfie.jpg',
});
try {
const response = await fetch('https://app.verifyhuman.io/api/verify', {
method: 'POST',
body: formData,
headers: {
'Content-Type': 'multipart/form-data',
},
});
const result = await response.json();
if (result.status === 'PASS') {
setStatus(`✓ Verified (${result.confidence}%)`);
// Send token to your backend
await validateOnBackend(result.token);
} else {
setStatus(`✗ Failed: ${result.error}`);
}
} catch (error) {
setStatus(`Error: ${error.message}`);
}
};
const validateOnBackend = async (token) => {
await fetch('https://your-api.com/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token }),
});
};
return (
<View style={{ padding: 20 }}>
<Button title="Verify Your Identity" onPress={pickImageAndVerify} />
{status && <Text style={{ marginTop: 20 }}>{status}</Text>}
</View>
);
}
Flutter
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:io';
class VerificationScreen extends StatefulWidget {
@override
_VerificationScreenState createState() => _VerificationScreenState();
}
class _VerificationScreenState extends State<VerificationScreen> {
String _status = '';
final ImagePicker _picker = ImagePicker();
Future<void> _verifyIdentity() async {
// Take photo
final XFile? image = await _picker.pickImage(
source: ImageSource.camera,
imageQuality: 80,
);
if (image == null) return;
setState(() => _status = 'Verifying...');
try {
// Create multipart request
var request = http.MultipartRequest(
'POST',
Uri.parse('https://app.verifyhuman.io/api/verify'),
);
request.fields['clientKey'] = 'pk_live_your_key_here';
request.files.add(await http.MultipartFile.fromPath('file', image.path));
// Send request
var response = await request.send();
var responseData = await response.stream.toBytes();
var result = json.decode(String.fromCharCodes(responseData));
if (result['status'] == 'PASS') {
setState(() => _status = '✓ Verified (${result['confidence']}%)');
// Validate on backend
await _validateOnBackend(result['token']);
} else {
setState(() => _status = '✗ Failed: ${result['error']}');
}
} catch (e) {
setState(() => _status = 'Error: $e');
}
}
Future<void> _validateOnBackend(String token) async {
await http.post(
Uri.parse('https://your-api.com/validate'),
headers: {'Content-Type': 'application/json'},
body: json.encode({'token': token}),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Verification')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _verifyIdentity,
child: Text('Verify Your Identity'),
),
if (_status.isNotEmpty)
Padding(
padding: EdgeInsets.all(20),
child: Text(_status, style: TextStyle(fontSize: 16)),
),
],
),
),
);
}
}
Error Handling
Always handle errors gracefully:
async function verifyWithErrorHandling(file) {
try {
const formData = new FormData();
formData.append('clientKey', 'pk_live_your_key');
formData.append('file', file);
const response = await fetch('https://app.verifyhuman.io/api/verify', {
method: 'POST',
body: formData
});
const result = await response.json();
switch (response.status) {
case 200:
if (result.status === 'PASS') {
return { success: true, data: result };
} else {
return { success: false, error: result.error };
}
case 400:
return { success: false, error: 'Invalid request: ' + result.error };
case 401:
return { success: false, error: 'Authentication failed: ' + result.error };
case 402:
return { success: false, error: 'No credits remaining. Please upgrade your plan.' };
case 429:
if (result.status === 'throttled') {
return { success: false, error: 'Too many attempts. Please try again in 5 minutes.' };
}
return { success: false, error: 'Rate limit exceeded' };
case 500:
return { success: false, error: 'Server error. Please try again later.' };
default:
return { success: false, error: 'Unexpected error occurred' };
}
} catch (error) {
return { success: false, error: 'Network error: ' + error.message };
}
}
Best Practices
1. Always Validate on Backend
// ✅ GOOD - Validate on backend
const frontendResult = await verifyOnFrontend(file);
const backendValidation = await validateOnBackend(frontendResult.token);
// ❌ BAD - Never trust frontend only
if (frontendResult.status === 'PASS') {
grantAccess(); // Don't do this!
}
2. Handle Throttling Gracefully
function showUserFriendlyError(result) {
if (result.status === 'throttled') {
return 'Too many attempts. Please wait 5 minutes and try again with better lighting.';
}
return result.error || 'Verification failed';
}
3. Provide Clear User Instructions
<div class="verification-instructions">
<h3>Tips for Successful Verification</h3>
<ul>
<li>Use good lighting</li>
<li>Face the camera directly</li>
<li>Remove glasses or masks</li>
<li>Ensure only your face is visible</li>
<li>Hold still while taking the photo</li>
</ul>
</div>
4. Store Verification Status
// After successful verification
await db.users.update({
id: userId,
verified: true,
verified_at: new Date(),
verification_id: result.verification_id,
verification_confidence: result.confidence
});
Widget Integration
For a faster, no-code approach, use our embeddable JavaScript widgets. Widgets handle camera access, image capture, and verification UI automatically.
VerifyHuman Widget
<script src="https://app.verifyhuman.io/widget.js"></script>
<div id="verifyhuman-widget"></div>
<script>
VerifyHuman.init({
apiKey: 'vhk-a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6',
buttonText: 'Verify You\'re Human',
onSuccess: function(result) {
console.log('Verified!', result);
},
onFailure: function(error) {
console.error('Failed:', error);
}
});
</script>
VerifyAge Widget
<script src="https://app.verifyhuman.io/widget-age.js"></script>
<div id="verifyage-widget"></div>
<script>
VerifyAge.init({
apiKey: 'vhk-a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6',
useCase: 'alcohol',
minAge: 21,
onSuccess: function(result) {
console.log('Age verified!', result);
}
});
</script>
VerifyIdentity Widget
<script src="https://app.verifyhuman.io/widget-identity.js"></script>
<div id="verifyidentity-widget"></div>
<script>
VerifyIdentity.init({
apiKey: 'vhk-a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6',
requireIdBack: false,
onSuccess: function(result) {
console.log('Identity verified!', result);
}
});
</script>
VerifyCompliance Widget
<script src="https://app.verifyhuman.io/widget-compliance.js"></script>
<div id="verifycompliance-widget"></div>
<script>
VerifyCompliance.init({
apiKey: 'vhk-a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6',
onSuccess: function(result) {
if (result.compliance_screening.total_hits === 0) {
console.log('Cleared!', result);
} else {
console.warn('Compliance hits detected');
}
}
});
</script>
Full Widget Documentation: Widget Integration Guide
Hosted Flows (No-Code Integration)
For the fastest implementation without any coding, use our hosted verification pages. Perfect for non-technical teams or quick proof-of-concepts.
How It Works
- Log in to your Dashboard
- Navigate to Hosted Flows
- Click "Create New Flow"
- Select your verification product
- Configure settings and branding
- Generate and share the secure URL
Example Use Cases
Customer Onboarding:
https://verify.verifyhuman.io/flow/ver_kyc_abc123xyz456
Send this link to customers via email. After verification, they're redirected to your success URL.
Age-Gated Content:
https://verify.verifyhuman.io/flow/age_def456ghi789
Display this link or QR code for age verification before content access.
Event Check-In:
https://verify.verifyhuman.io/flow/vh_jkl012mno345
Show QR code at entrance for contactless bot-free check-in.
Customization Options
- Custom branding (logo, colors)
- Custom messages and instructions
- Conditional redirects (success/failure)
- Webhook notifications
- Single-use or reusable links
- Auto-expiration (24 hours to 30 days)
Full Hosted Flows Documentation: Hosted Flows Guide
Product Selection Guide
Choose the right verification product for your use case:
| Product | Use Case | Credit Cost | API Endpoint |
|---|---|---|---|
| VerifyHuman | Bot detection, liveness | 1 credit | /api/verify |
| VerifyAge | Age verification, compliance | 10 credits | /api/v1/verify-age/* |
| VerifyIdentity | KYC, ID verification | 30 credits | /api/identity/v1/submit |
| VerifyCompliance | KYC+ with AML/sanctions | 50 credits | /api/kyc/v1/submit_plus |
Multi-Product Integration Example
// Determine which product to use based on business logic
async function verifyUser(userData, requirementLevel) {
switch(requirementLevel) {
case 'basic':
// Just check if real human
return await verifyHuman(userData.selfie);
case 'age-restricted':
// Verify age for age-gated content
return await verifyAge(userData.selfie, 21);
case 'kyc':
// Full identity verification
return await verifyIdentity(userData.selfie, userData.idFront);
case 'compliance':
// KYC + AML/PEP/Sanctions screening
return await verifyCompliance(userData.selfie, userData.idFront);
}
}
Product-Specific Guides:
Testing Your Integration
Local Testing
- Use your test API keys
- Test with sample images
- Verify error handling
- Check token validation flow
Pre-Production Checklist
- API keys stored in environment variables
- Client secret never exposed in frontend
- Backend token validation implemented
- Error handling covers all status codes
- User feedback messages are clear
- Throttling handled gracefully
- Database stores verification status
- Production keys configured
Next Steps
- API Reference - Complete API documentation
- Authentication - Security best practices
- FAQ - Common questions and troubleshooting
Support
Need integration help?
- Email: support@verifyhuman.io
- Live Chat: Available in your dashboard