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
Copy
npm install @vouch-in/js
Quick Start
Copy
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 {
// Check individual validations
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
Copy
import { Vouch } from '@vouch-in/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")
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:Copy
const result = await vouch.validate(email);
Email address to validate
Optional validation request options:
fingerprintHash- Override the auto-generated fingerprint haship- Client IP address (usually set server-side)userAgent- Client user agent (usually set server-side)
Promise<ValidationResponse>
Copy
interface ValidationResponse {
checks: Record<string, CheckResult>;
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>;
}
generateFingerprint()
Get the device fingerprint directly:Copy
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
Promise<Fingerprint>
Copy
interface Fingerprint {
hardware: HardwareSignals;
canvas: CanvasSignals;
webgl: WebGLSignals;
audio?: AudioSignals;
fonts: FontSignals;
browser: BrowserSignals;
storage: StorageSignals;
}
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-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
Copy
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
Copy
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
Copy
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
Copy
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
Copy
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
Copy
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:Copy
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
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-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
Copy
<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>