Conure API

React bot detection and email validation

React runs in the browser, so it must not hold your API key. Call Conure from your own backend and expose a thin verdict endpoint to the client.

1. Install

No runtime dependency. The hook below uses the platform fetch.

2. Add the file

src/hooks/useEmailRisk.ts

import { useCallback, useEffect, useRef, useState } from "react";

export interface EmailVerdict {
  is_risky: boolean;
  risk_score: number;
  reasons: string[];
}

/**
 * Talks to YOUR backend (/api/email-risk), which holds the Conure key.
 * Debounced, and aborts the in-flight request when the input changes.
 */
export function useEmailRisk(email: string, delayMs = 400) {
  const [verdict, setVerdict] = useState<EmailVerdict | null>(null);
  const [loading, setLoading] = useState(false);
  const inFlight = useRef<AbortController | null>(null);

  const check = useCallback(async (value: string) => {
    inFlight.current?.abort();
    const controller = new AbortController();
    inFlight.current = controller;
    setLoading(true);
    try {
      const response = await fetch("/api/email-risk", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email: value }),
        signal: controller.signal,
      });
      setVerdict(response.ok ? ((await response.json()) as EmailVerdict) : null);
    } catch {
      setVerdict(null); // fail open: never block signup on a network error
    } finally {
      if (!controller.signal.aborted) setLoading(false);
    }
  }, []);

  useEffect(() => {
    if (!email.includes("@")) {
      setVerdict(null);
      return;
    }
    const timer = setTimeout(() => void check(email), delayMs);
    return () => clearTimeout(timer);
  }, [email, delayMs, check]);

  return { verdict, loading };
}

3. Wire it up

function SignupForm() {
  const [email, setEmail] = useState("");
  const { verdict, loading } = useEmailRisk(email);

  return (
    <>
      <input value={email} onChange={(event) => setEmail(event.target.value)} />
      {loading && <span>Checking...</span>}
      {verdict?.is_risky && <p role="alert">Please use a permanent email address.</p>}
      <button disabled={verdict?.is_risky === true}>Create account</button>
    </>
  );
}

4. Verify

Confirm the API answers before you debug your React wiring. The sample address sits inside a published AWS range, so a correct setup returns is_bot: true.

Notes

Other frameworks