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-in/js
Browser Support: All modern browsers (Chrome 90+, Firefox 88+, Safari 14+, Edge 90+)
Installation
Quick Start
import { Vouch } from '@vouch-in/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]');
// Check the recommendation
if (result.recommendation === 'allow') {
console.log('Email is valid!');
} else {
// Option 1: Use the message field for a user-friendly error
alert(result.message); // e.g. "Please use a permanent email address, not a temporary one."
// Option 2: Check specific validations for custom messages
const { checks } = result;
if (!checks.syntax?.pass) {
alert('Invalid email format');
} else if (!checks.disposable?.pass) {
alert('Disposable emails are not allowed');
}
}
Initialization
Basic Initialization
import { Vouch } from '@vouch-in/js';
const vouch = new Vouch(projectId, apiKey);
With Options
const vouch = new Vouch(projectId, apiKey, {
// API endpoint (default: https://api.vouch.expert)
endpoint: 'https://api.vouch.expert',
// API version (default: "latest")
apiVersion: 'latest', // or 1, 2, etc.
});
Device fingerprinting starts automatically when the Vouch instance is created. This ensures the fingerprint is ready when you call validate().
Core Methods
validate()
Validate an email address with automatic device fingerprinting:
const result = await vouch.validate(email);
Parameters:
Email address to validate
Optional validation request options:
fingerprintHash - Override the auto-generated fingerprint hash
ip - Client IP address (usually set server-side)
userAgent - Client user agent (usually set server-side)
Returns: Promise<ValidationResponse>
interface ValidationResponse {
checks: Record<string, CheckResult>;
message: string | null;
metadata: {
fingerprintHash: string | null;
previousSignups: number;
totalLatency: number;
};
recommendation: 'allow' | 'block' | 'flag';
signals: string[];
}
interface CheckResult {
pass: boolean;
error?: string;
latency: number;
metadata?: Record<string, unknown>;
}
The message field contains a human-friendly error message when the recommendation is flag or block (e.g. “Please use a permanent email address, not a temporary one.”). It is null when the recommendation is allow.
generateFingerprint()
Get the device fingerprint directly:
const fingerprint = await vouch.generateFingerprint();
console.log(fingerprint.canvas.hash); // Canvas fingerprint hash
console.log(fingerprint.webgl.hash); // WebGL fingerprint hash
console.log(fingerprint.hardware.screen); // Screen dimensions
Returns: Promise<Fingerprint>
interface Fingerprint {
hardware: HardwareSignals;
canvas: CanvasSignals;
webgl: WebGLSignals;
audio?: AudioSignals;
fonts: FontSignals;
browser: BrowserSignals;
storage: StorageSignals;
}
Usage Examples
<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-in/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);
const { checks, recommendation } = result;
// Check for specific validation failures
if (!checks.syntax?.pass) {
errorDiv.textContent = 'Invalid email format';
errorDiv.classList.remove('hidden');
return;
}
if (!checks.disposable?.pass) {
errorDiv.textContent = 'Disposable emails are not allowed';
errorDiv.classList.remove('hidden');
return;
}
// Log suspicious patterns based on recommendation
if (recommendation === 'flag') {
console.warn('Email flagged for review:', result);
}
// 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
import { Vouch } from '@vouch-in/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.checks.syntax?.pass) {
emailInput.classList.add('error');
showError('Invalid email format');
} else if (result.recommendation !== 'allow') {
emailInput.classList.add('warning');
showWarning('This email may have issues');
} else {
emailInput.classList.remove('error', 'warning');
hideError();
}
}, 500); // Wait 500ms after user stops typing
});
Check Specific Validations
const result = await vouch.validate('[email protected]');
const { checks, metadata, recommendation } = result;
// Check specific validations
if (!checks.disposable?.pass) {
alert('Temporary emails are not allowed');
}
if (!checks.mx?.pass) {
alert('Email domain has no mail servers');
}
// Check device history
if (metadata.previousSignups > 5) {
console.warn('Multiple signups from this device');
}
Error Handling
try {
const result = await vouch.validate(email);
// Check validation results
const { checks, recommendation } = result;
if (!checks.syntax?.pass) {
throw new Error('Invalid email syntax');
}
if (!checks.disposable?.pass) {
throw new Error('Disposable email not allowed');
}
if (recommendation === 'block') {
throw new Error('This email cannot be used');
}
// Proceed with valid email
console.log('Email validated successfully');
} catch (error) {
if (error.message === 'Invalid email format') {
// Client-side validation failed
console.error('Email format is invalid');
} else {
// Network or other error
console.error('Validation error:', error.message);
}
}
Advanced Features
Custom Validation Logic
async function validateEmailWithCustomRules(email) {
const result = await vouch.validate(email);
const { checks, metadata, recommendation } = result;
// Custom business logic
if (!checks.disposable?.pass) {
return {
valid: false,
reason: 'Disposable emails not allowed'
};
}
if (metadata.previousSignups > 10) {
return {
valid: false,
reason: 'Too many accounts from this device'
};
}
if (recommendation === 'flag') {
return {
valid: 'review',
reason: 'Email flagged for manual review'
};
}
return {
valid: true,
reason: 'Email is valid'
};
}
Batch Validation
async function validateMultipleEmails(emails) {
const results = await Promise.all(
emails.map(email => vouch.validate(email))
);
return results.map((result, index) => ({
email: emails[index],
recommendation: result.recommendation,
checks: result.checks,
previousSignups: result.metadata.previousSignups
}));
}
// Usage
const emails = [
'[email protected]',
'[email protected]',
'[email protected]'
];
const results = await validateMultipleEmails(emails);
console.log(results);
Retry Logic
async function validateWithRetry(email, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const result = await vouch.validate(email);
return result;
} catch (error) {
if (attempt < maxRetries) {
const delay = 1000 * attempt;
console.log(`Retrying in ${delay}ms... (attempt ${attempt}/${maxRetries})`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}
TypeScript Support
The SDK is written in TypeScript and includes full type definitions:
import { Vouch, ValidationResponse, CheckResult, Fingerprint } from '@vouch-in/js';
const vouch = new Vouch(projectId, apiKey);
const result: ValidationResponse = await vouch.validate('[email protected]');
// Type-safe access to validation results
const { checks, metadata, recommendation } = result;
const disposableCheck: CheckResult | undefined = checks.disposable;
if (disposableCheck && !disposableCheck.pass) {
console.log('Disposable email detected');
}
if (metadata.previousSignups > 5) {
console.log('Multiple signups from this device');
}
// Get fingerprint with full types
const fingerprint: Fingerprint = await vouch.generateFingerprint();
console.log(fingerprint.canvas.hash);
Browser Compatibility
For older browsers, use a polyfill for fetch and Promise.
Common Patterns
Vue.js
<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-in/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.checks.syntax?.pass) {
this.error = 'Invalid email format';
return;
}
if (!result.checks.disposable?.pass) {
this.error = 'Disposable emails not allowed';
return;
}
// Proceed with signup
await this.createAccount(this.email);
} catch (error) {
this.error = 'Validation failed';
} finally {
this.loading = false;
}
}
}
};
</script>
Svelte
<script>
import { Vouch } from '@vouch-in/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.checks.syntax?.pass) {
error = 'Invalid email format';
return;
}
if (!result.checks.disposable?.pass) {
error = 'Disposable emails not allowed';
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>
Next Steps