> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vouch.expert/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript SDK

> Vouch SDK for vanilla JavaScript and browser applications

## 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.

<Note>
  **Package:** `@vouch-in/js`
  **Browser Support:** All modern browsers (Chrome 90+, Firefox 88+, Safari 14+, Edge 90+)
</Note>

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @vouch-in/js
  ```

  ```bash yarn theme={null}
  yarn add @vouch-in/js
  ```

  ```bash pnpm theme={null}
  pnpm add @vouch-in/js
  ```
</CodeGroup>

## Quick Start

```javascript theme={null}
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('user@example.com');

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

```javascript theme={null}
import { Vouch } from '@vouch-in/js';

const vouch = new Vouch(projectId, apiKey);
```

### With Options

```javascript theme={null}
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.
});
```

<Note>
  Device fingerprinting starts automatically when the Vouch instance is created. This ensures the fingerprint is ready when you call `validate()`.
</Note>

## Core Methods

### validate()

Validate an email address with automatic device fingerprinting:

```javascript theme={null}
const result = await vouch.validate(email);
```

**Parameters:**

<ParamField path="email" type="string" required>
  Email address to validate
</ParamField>

<ParamField path="options" type="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)
</ParamField>

**Returns:** `Promise<ValidationResponse>`

```typescript theme={null}
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>;
}
```

<Note>
  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`.
</Note>

### generateFingerprint()

Get the device fingerprint directly:

```javascript theme={null}
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>`

```typescript theme={null}
interface Fingerprint {
  hardware: HardwareSignals;
  canvas: CanvasSignals;
  webgl: WebGLSignals;
  audio?: AudioSignals;
  fonts: FontSignals;
  browser: BrowserSignals;
  storage: StorageSignals;
}
```

## Usage Examples

### Form Validation

```html theme={null}
<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

```javascript theme={null}
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

```javascript theme={null}
const result = await vouch.validate('user@example.com');

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

```javascript theme={null}
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

```javascript theme={null}
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

```javascript theme={null}
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 = [
  'user1@gmail.com',
  'user2@tempmail.com',
  'user3@company.com'
];

const results = await validateMultipleEmails(emails);
console.log(results);
```

### Retry Logic

```javascript theme={null}
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:

```typescript theme={null}
import { Vouch, ValidationResponse, CheckResult, Fingerprint } from '@vouch-in/js';

const vouch = new Vouch(projectId, apiKey);

const result: ValidationResponse = await vouch.validate('user@example.com');

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

<Note>
  For older browsers, use a polyfill for `fetch` and `Promise`.
</Note>

## Common Patterns

### Vue.js

```vue theme={null}
<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

```svelte theme={null}
<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

<CardGroup cols={2}>
  <Card title="React SDK" icon="react" href="/sdks/react">
    React hooks and components
  </Card>

  <Card title="Node.js SDK" icon="node" href="/sdks/node">
    Server-side validation
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/validate">
    REST API documentation
  </Card>

  <Card title="Best Practices" icon="star" href="/guides/best-practices">
    Production deployment tips
  </Card>
</CardGroup>
