:::note[Fail-open by default]
Every example on this page wraps the Syvel call in a `try/catch`. If the API is unreachable (network error, timeout, quota exceeded), the check is **skipped and the user is let through**. Never let a third-party service block a sign-up.
:::

## Installation

```bash
npm install @syvel/js
```

**Requirements:** Node.js 18+, modern browsers, Deno. Zero external dependencies.

## Basic usage

```typescript
import { Syvel } from "@syvel/js";

const syvel = new Syvel({ apiKey: "sv_your_key" });

try {
  // Check a full email address (analyzes local-part patterns too)
  const result = await syvel.checkEmail("user@yopmail.com");

  console.log(result.risk_score);  // 100
  console.log(result.is_risky);    // true
  console.log(result.reason);      // "disposable"

  // Or check a bare domain
  const domainResult = await syvel.check("yopmail.com");
} catch {
  // API unavailable — let the user through
}
```

## Response format

```typescript
interface CheckResult {
  email: string;                 // Masked email (local part never exposed)
  is_risky: boolean;             // true if risk_score exceeds your project threshold (default 65)
  risk_score: number;            // 0–100  |  0–29 safe  |  30–49 low  |  50–79 warn  |  80+ block
  reason: string;                // "safe" | "disposable" | "undeliverable" | "role_account"
  deliverability_score: number;  // 0–100 likelihood of successful delivery
  did_you_mean?: string;         // Typo suggestion, e.g. "gmail.com" for "gmial.com"
  is_free_provider: boolean;     // Gmail, Yahoo, etc.
  is_corporate_email: boolean;   // Professional domain with business MX
  is_alias_email: boolean;       // Privacy relay (SimpleLogin, etc.)
  mx_provider_label: string;     // Human-readable MX provider name
}
```

## Configuration

```typescript
const syvel = new Syvel({
  apiKey: "sv_your_key",

  // Timeout in ms — default 3000. Keep it low: the API responds in < 300 ms.
  timeout: 3000,

  // Silent mode: returns null on any error instead of throwing.
  // Enables automatic fail-open without a try/catch.
  silent: true,
});
```

With `silent: true`, errors return `null` instead of throwing:

```typescript
const syvel = new Syvel({ apiKey: "sv_your_key", silent: true });

const result = await syvel.checkEmail("user@example.com");
if (result?.is_risky) {
  // Block or warn
}
// If result is null, the API was unavailable — let the user through automatically
```

## Error handling

```typescript
import {
  Syvel,
  SyvelTimeoutError,
  SyvelAuthError,
  SyvelForbiddenError,
  SyvelValidationError,
  SyvelRateLimitError,
} from "@syvel/js";

try {
  const result = await syvel.checkEmail("user@example.com");
} catch (error) {
  if (error instanceof SyvelTimeoutError) {
    // Always fail open on timeout
  } else if (error instanceof SyvelRateLimitError) {
    // error.resetAt → ISO 8601 timestamp of quota reset
    // Always fail open on quota exceeded
  } else if (error instanceof SyvelValidationError) {
    // Malformed email or domain input
  } else if (error instanceof SyvelAuthError) {
    // Invalid or missing API key (401)
  } else if (error instanceof SyvelForbiddenError) {
    // Request origin not in your API key's allowed list (403)
  }
}
```

| Error class | HTTP | When to fail open |
|---|---|---|
| `SyvelTimeoutError` | — | Always |
| `SyvelRateLimitError` | 429 | Always — check `resetAt` |
| `SyvelAuthError` | 401 | Fix your API key |
| `SyvelForbiddenError` | 403 | Check allowed origins |
| `SyvelValidationError` | 422 | Malformed input |

## HTML form with real-time validation

```html
<form id="signup-form">
  <input type="email" id="email" placeholder="your@email.com" />
  <p id="email-error" style="color: red; display: none;"></p>
  <button type="submit">Sign up</button>
</form>

<script type="module">
  import { Syvel } from "https://cdn.jsdelivr.net/npm/@syvel/js";

  const syvel = new Syvel({ apiKey: "sv_your_key", silent: true });
  const emailInput = document.getElementById("email");
  const emailError = document.getElementById("email-error");
  let debounceTimer;

  emailInput.addEventListener("input", () => {
    clearTimeout(debounceTimer);
    debounceTimer = setTimeout(async () => {
      const email = emailInput.value;
      if (!email.includes("@")) return;

      const result = await syvel.checkEmail(email);
      if (result?.is_risky) {
        emailError.textContent = "Please use a professional email address.";
        emailError.style.display = "block";
      } else {
        emailError.style.display = "none";
      }
    }, 500);
  });
</script>
```

## React hook

```tsx
import { useState, useCallback } from "react";
import { Syvel, type CheckResult } from "@syvel/js";

const syvel = new Syvel({
  apiKey: import.meta.env.VITE_SYVEL_API_KEY,
  silent: true,
});

export function useEmailCheck() {
  const [result, setResult] = useState<CheckResult | null>(null);
  const [loading, setLoading] = useState(false);

  const check = useCallback(async (email: string) => {
    if (!email.includes("@")) return;
    setLoading(true);
    const data = await syvel.checkEmail(email); // null on any error (silent mode)
    setResult(data);
    setLoading(false);
  }, []);

  return { result, loading, check };
}
```

## React Hook Form

```tsx
import { useForm } from "react-hook-form";
import { Syvel } from "@syvel/js";

const syvel = new Syvel({ apiKey: "sv_your_key", silent: true });

function SignupForm() {
  const { register, handleSubmit, setError, formState: { errors } } = useForm();

  const onSubmit = async (data: { email: string }) => {
    const result = await syvel.checkEmail(data.email);
    if (result?.is_risky) {
      setError("email", { message: "Please use a valid email address." });
      return;
    }
    // Continue with registration
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("email")} type="email" />
      {errors.email && <p>{errors.email.message}</p>}
      <button type="submit">Sign up</button>
    </form>
  );
}
```

## Express

```typescript
import express from "express";
import { Syvel } from "@syvel/js";

const app = express();
const syvel = new Syvel({ apiKey: process.env.SYVEL_API_KEY, silent: true });

app.post("/register", async (req, res) => {
  const result = await syvel.checkEmail(req.body.email);

  if (result?.is_risky) {
    return res.status(422).json({ error: "Please use a professional email address." });
  }
  // continue with registration…
});
```

## Fastify

```typescript
import Fastify from "fastify";
import { Syvel } from "@syvel/js";

const app = Fastify();
const syvel = new Syvel({ apiKey: process.env.SYVEL_API_KEY, silent: true });

app.post("/register", async (request, reply) => {
  const { email } = request.body as { email: string };
  const result = await syvel.checkEmail(email);

  if (result?.is_risky) {
    return reply.status(422).send({ error: "Please use a professional email address." });
  }
  // continue with registration…
});
```

## Next.js (Route Handler)

```typescript
import { NextRequest, NextResponse } from "next/server";
import { Syvel } from "@syvel/js";

const syvel = new Syvel({ apiKey: process.env.SYVEL_API_KEY, silent: true });

export async function POST(request: NextRequest) {
  const { email } = await request.json();
  const result = await syvel.checkEmail(email);

  if (result?.is_risky) {
    return NextResponse.json(
      { error: "Please use a professional email address." },
      { status: 422 }
    );
  }
  // continue with registration…
}
```

## Resources

- [npm — @syvel/js](https://www.npmjs.com/package/@syvel/js)
- [GitHub — syvel-js](https://github.com/Syvel-io/syvel-js)
- [JavaScript (Fetch) — no SDK required](/docs/integrations/javascript-fetch)
- [API reference — Domain check](/docs/api/check)
- [Authentication](/docs/guides/authentication)
- [Error codes](/docs/guides/errors)
- [Rate limits](/docs/guides/rate-limits)