auth

Work-email checks with Better Auth

Add an isBusinessEmail check to Better Auth sign-ups: a reusable server helper you call from a hook that runs before a user is created.

isBusinessEmail for Better Auth

Better Auth is a TypeScript auth library that runs inside your own server, so the check runs in your code, with your secret key, before the user exists. Better Auth lets you run code before endpoints execute and before database records are created. Its “Hooks” and “Database hooks” docs show the current signatures. Call the helper below from whichever fits your setup.

1. The helper

// lib/work-email.ts
export type Verdict = {
  category?: string;
  recommendation: 'allow' | 'review' | 'block';
  did_you_mean?: string | null;
  workspace?: {
    google_workspace?: { detected: boolean };
    microsoft_365?: { detected: boolean; tenant_id: string | null };
  };
  failOpen?: boolean;
};

export async function checkWorkEmail(email: string): Promise<Verdict> {
  try {
    const res = await fetch('https://api.isbusinessemail.com/v1/check', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.IBE_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ email }),
      signal: AbortSignal.timeout(2500),
    });
    if (!res.ok) return { recommendation: 'allow', failOpen: true };
    return (await res.json()) as Verdict;
  } catch {
    return { recommendation: 'allow', failOpen: true };
  }
}

2. Where to call it

Hook point Covers On block
Before the email sign-up endpoint runs Email/password sign-ups Throw an error with a friendly message; the client receives it
Before a user record is created Every new user, including social and magic-link sign-ins Abort the creation, or allow it and store a “needs work email” flag

In both cases the logic is the same:

const verdict = await checkWorkEmail(email);

if (verdict.recommendation === 'block') {
  // reject with a message such as:
  // "Please use your work email." + (verdict.did_you_mean ? ` Did you mean ${verdict.did_you_mean}?` : '')
}
// otherwise continue, and persist verdict.category (and workspace detection) on the user

If you store the verdict on the user, add columns or additional user fields for email_category, google_workspace and microsoft_365, so your app can route onboarding without calling the API again.

Tips

  • Social sign-ins skip the email endpoint, so the “before user created” hook point is the one that catches everything.
  • Fail open on timeouts and mark the user for a background re-check.
  • Test with personal@test.isbusinessemail.com and workspace@test.isbusinessemail.com (test addresses).

Official docs: Better Auth documentation. See also: Signup form guide.