Skip to main content

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

npm install @vouch-in/js

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 {
  // 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

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
string
required
Email address to validate
options
object
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>;
  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:
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 Validation

<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