Overview
The Vouch JavaScript SDK provides email validation and device fingerprinting for browser-based applications. It’s framework-agnostic and works with any JavaScript project.Package:
@vouch/js
Browser Support: All modern browsers (Chrome 90+, Firefox 88+, Safari 14+, Edge 90+)Installation
Copy
npm install @vouch/js
Quick Start
Copy
import { Vouch } from '@vouch/js';
// Initialize with your client API key
const vouch = new Vouch(
'your-project-id',
'your-client-api-key'
);
// Validate an email
const result = await vouch.validate('[email protected]');
if (!result.success) {
alert('Validation failed: ' + result.error);
} else if (result.data) {
// Check individual validations
const { checks, deviceData, ipData } = result.data;
if (!checks.syntax?.pass) {
alert('Invalid email format');
} else if (!checks.disposable?.pass) {
alert('Disposable emails are not allowed');
} else {
console.log('Email is valid!');
}
}
Initialization
Basic Initialization
Copy
import { Vouch } from '@vouch/js';
const vouch = new Vouch(projectId, apiKey);
With Options
Copy
const vouch = new Vouch(projectId, apiKey, {
// API endpoint (default: https://api.vouch.expert)
endpoint: 'https://api.vouch.expert',
// API version (default: "latest")
version: 'latest', // or 1, 2, etc.
// Custom fingerprint configuration
fingerprintOptions: {
enableCanvas: true,
enableWebGL: true,
enableAudio: true,
enableFonts: true
}
});
Core Methods
validate()
Validate an email address with automatic device fingerprinting:Copy
const result = await vouch.validate(email);
Email address to validate
The JavaScript/browser SDK automatically includes device fingerprinting. The validate method does not accept additional options - use server-side SDKs for advanced options.
Promise<ValidationResponse>
Copy
interface ValidationResponse {
success: boolean;
error?: string;
statusCode?: number;
data?: {
checks: Record<string, CheckResult>;
deviceData: DeviceData | null;
ipData: IPData | null;
signals: string[];
};
}
interface CheckResult {
pass: boolean;
error?: string;
latency: number;
metadata?: Record<string, unknown>;
}
interface DeviceData {
isKnownDevice: boolean;
isNewEmail: boolean;
emailsUsed: number;
previousSignups: number;
firstSeen: number;
lastSeen?: number;
}
interface IPData {
ip: string;
isVPN: boolean;
isFraud: boolean;
}
getFingerprint()
Get device fingerprint without validating an email:Copy
const fingerprint = await vouch.getFingerprint();
console.log(fingerprint.visitorId); // Unique device ID
console.log(fingerprint.confidence); // 0-1 confidence score
Promise<Fingerprint>
Copy
interface Fingerprint {
visitorId: string;
confidence: number;
signals: {
userAgent: string;
screenResolution: string;
timezone: string;
language: string;
// ... 100+ signals
};
}
Usage Examples
Form Validation
Copy
<form id="signup-form">
<input type="email" id="email" required>
<button type="submit">Sign Up</button>
<div id="error" class="hidden"></div>
</form>
<script type="module">
import { Vouch } from '@vouch/js';
const vouch = new Vouch(PROJECT_ID, API_KEY);
const form = document.getElementById('signup-form');
const emailInput = document.getElementById('email');
const errorDiv = document.getElementById('error');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const email = emailInput.value;
try {
const result = await vouch.validate(email);
if (!result.success) {
errorDiv.textContent = result.error || 'Validation failed';
errorDiv.classList.remove('hidden');
return;
}
if (result.data) {
const { checks } = result.data;
// Check for specific validation failures
if (!checks.syntax?.pass || !checks.disposable?.pass) {
errorDiv.textContent = 'This email address cannot be used';
errorDiv.classList.remove('hidden');
return;
}
// Log suspicious patterns
if (!checks.deviceFingerprint?.pass || !checks.ipReputation?.pass) {
console.warn('Email flagged for review:', result.data);
}
}
// Proceed with signup
submitForm(email);
} catch (error) {
console.error('Validation error:', error);
errorDiv.textContent = 'Unable to validate email';
errorDiv.classList.remove('hidden');
}
});
</script>
Real-time Validation
Copy
import { Vouch } from '@vouch/js';
const vouch = new Vouch(PROJECT_ID, API_KEY);
const emailInput = document.querySelector('input[type="email"]');
// Debounce validation
let timeout;
emailInput.addEventListener('input', (e) => {
clearTimeout(timeout);
const email = e.target.value;
// Only validate if email looks valid
if (!email.includes('@')) return;
timeout = setTimeout(async () => {
const result = await vouch.validate(email);
// Show inline feedback
if (!result.success || !result.data?.checks.syntax?.pass) {
emailInput.classList.add('error');
showError('This email cannot be used');
} else {
emailInput.classList.remove('error');
hideError();
}
}, 500); // Wait 500ms after user stops typing
});
Check Specific Validations
Copy
const result = await vouch.validate('[email protected]');
if (result.success && result.data) {
const { checks } = result.data;
// Check specific validations
if (!checks.disposable?.pass) {
alert('Temporary emails are not allowed');
}
if (!checks.mx?.pass) {
alert('Email domain has no mail servers');
}
if (checks.catchall?.pass === false) {
console.warn('Domain accepts all emails');
}
}
Error Handling
Copy
try {
const result = await vouch.validate(email);
if (!result.success) {
console.error('Validation failed:', result.error);
// Handle specific HTTP status codes
if (result.statusCode === 400) {
console.error('Invalid email format');
} else if (result.statusCode === 401) {
console.error('Invalid API key');
} else if (result.statusCode === 402) {
console.error('Validation quota exceeded');
} else if (result.statusCode === 429) {
console.error('Too many requests, please retry later');
}
return;
}
// Check validation results
if (result.data) {
const { checks, deviceData, ipData } = result.data;
if (!checks.syntax?.pass) {
throw new Error('Invalid email syntax');
}
if (!checks.disposable?.pass) {
throw new Error('Disposable email not allowed');
}
}
} catch (error) {
console.error('Validation error:', error.message);
}
Advanced Features
Custom Validation Logic
Copy
async function validateEmailWithCustomRules(email) {
const result = await vouch.validate(email);
if (!result.success || !result.data) {
return {
valid: false,
reason: result.error || 'Validation failed'
};
}
const { checks, deviceData, ipData } = result.data;
// Custom business logic
if (!checks.disposable?.pass) {
return {
valid: false,
reason: 'Disposable emails not allowed'
};
}
if (deviceData && deviceData.emailsUsed > 10) {
return {
valid: false,
reason: 'Too many accounts from this device'
};
}
if (ipData?.isVPN) {
return {
valid: 'flag',
reason: 'VPN detected - requires verification'
};
}
return {
valid: true,
reason: 'Email is valid'
};
}
Batch Validation
Copy
async function validateMultipleEmails(emails) {
const results = await Promise.all(
emails.map(email => vouch.validate(email))
);
return results.map((result, index) => ({
email: emails[index],
success: result.success,
checks: result.data?.checks,
deviceData: result.data?.deviceData,
error: result.error
}));
}
// Usage
const emails = [
'[email protected]',
'[email protected]',
'[email protected]'
];
const results = await validateMultipleEmails(emails);
console.log(results);
Retry Logic
Copy
async function validateWithRetry(email, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
const result = await vouch.validate(email);
if (result.success) {
return result;
}
// Retry on rate limit or server errors
if (result.statusCode === 429 || result.statusCode >= 500) {
if (attempt < maxRetries) {
const delay = 1000 * attempt;
console.log(`Retrying in ${delay}ms... (attempt ${attempt}/${maxRetries})`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
}
// Don't retry on client errors (400, 401, etc)
return result;
}
}
TypeScript Support
The SDK is written in TypeScript and includes full type definitions:Copy
import { Vouch, ValidationResponse, CheckResult, DeviceData } from '@vouch/js';
const vouch = new Vouch(projectId, apiKey);
const result: ValidationResponse = await vouch.validate('[email protected]');
if (result.success && result.data) {
// Type-safe access to validation results
const { checks, deviceData, ipData } = result.data;
const disposableCheck: CheckResult | undefined = checks.disposable;
if (disposableCheck && !disposableCheck.pass) {
console.log('Disposable email detected');
}
if (deviceData && deviceData.emailsUsed > 5) {
console.log('Multiple emails from this device');
}
}
Browser Compatibility
| Feature | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| Email Validation | 90+ | 88+ | 14+ | 90+ |
| Device Fingerprinting | 90+ | 88+ | 14+ | 90+ |
| Canvas Fingerprinting | 90+ | 88+ | 14+ | 90+ |
| WebGL Fingerprinting | 90+ | 88+ | 14+ | 90+ |
| Audio Fingerprinting | 90+ | 88+ | 14+ | 90+ |
For older browsers, use a polyfill for
fetch and Promise.CDN Usage
For projects without a build system:Copy
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.vouch.expert/js/v1/vouch.min.js"></script>
</head>
<body>
<script>
// Vouch is available globally
const vouch = new Vouch.Vouch(PROJECT_ID, API_KEY);
async function validateEmail(email) {
const result = await vouch.validate(email);
console.log(result);
}
</script>
</body>
</html>
Performance
- Initialization time: < 100ms
- Fingerprint collection: 200-500ms
- Validation request: 150-300ms
- Total time: 350-800ms
Common Patterns
Vue.js
Copy
<template>
<form @submit.prevent="handleSubmit">
<input v-model="email" type="email" required>
<button :disabled="loading">Sign Up</button>
<p v-if="error">{{ error }}</p>
</form>
</template>
<script>
import { Vouch } from '@vouch/js';
const vouch = new Vouch(PROJECT_ID, API_KEY);
export default {
data() {
return {
email: '',
loading: false,
error: null
};
},
methods: {
async handleSubmit() {
this.loading = true;
this.error = null;
try {
const result = await vouch.validate(this.email);
if (!result.success || !result.data) {
this.error = result.error || 'This email cannot be used';
return;
}
if (!result.data.checks.syntax?.pass || !result.data.checks.disposable?.pass) {
this.error = 'This email cannot be used';
return;
}
// Proceed with signup
await this.createAccount(this.email);
} catch (error) {
this.error = 'Validation failed';
} finally {
this.loading = false;
}
}
}
};
</script>
Svelte
Copy
<script>
import { Vouch } from '@vouch/js';
const vouch = new Vouch(PROJECT_ID, API_KEY);
let email = '';
let loading = false;
let error = null;
async function handleSubmit() {
loading = true;
error = null;
try {
const result = await vouch.validate(email);
if (!result.success || !result.data) {
error = result.error || 'This email cannot be used';
return;
}
if (!result.data.checks.syntax?.pass || !result.data.checks.disposable?.pass) {
error = 'This email cannot be used';
return;
}
// Proceed
await createAccount(email);
} catch (err) {
error = 'Validation failed';
} finally {
loading = false;
}
}
</script>
<form on:submit|preventDefault={handleSubmit}>
<input bind:value={email} type="email" required>
<button disabled={loading}>Sign Up</button>
{#if error}
<p>{error}</p>
{/if}
</form>