Bolt-on email verification for AI-coded apps.
One env var, one fetch call. No SDK to install, no schema to learn.
Paste the prompt below into your AI editor and your signup form
stops accepting test@asdf.com.
Drop this into Cursor or Claude
The prompt below is intentionally framework-agnostic. Your AI editor will adapt it to Next.js, Remix, Express, Fastify, Hono, FastAPI — whatever your project already uses.
Add an email verifier to my signup form.
When a user submits, call POST https://vrfymail.com/v1/verify
with Authorization: Bearer ${MAIL_VERIFIER_KEY} and JSON body:
{ "email": "<email>", "strict": true }
If the response result is "undeliverable", show the message:
"Please use a different email address."
If did_you_mean is non-null, suggest that correction inline.
Otherwise let the form submit normally. Plain fetch. No SDK.
Works on Node, Bun, Deno, Workers, edge runtimes — anywhere fetch does. Type-checked SDK is on the roadmap; until then this is all you need.
// app/api/signup/route.ts
import { NextResponse } from "next/server";
export async function POST(req: Request) {
const { email } = await req.json();
const r = await fetch("https://vrfymail.com/v1/verify", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.MAIL_VERIFIER_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ email, strict: true }),
});
const verdict = await r.json();
if (verdict.result === "undeliverable") {
return NextResponse.json(
{ error: verdict.did_you_mean
? `Did you mean ${verdict.did_you_mean}?`
: "Please use a different email." },
{ status: 400 }
);
}
// ... continue with your signup logic
} A 25-line hook for live validation
Show "Did you mean gmail.com?" inline as the user types. Debounce on your end and call this hook on blur — you don't need a library.
// useEmailVerify.ts — instant client-side feedback
import { useState } from "react";
export function useEmailVerify(apiBase = "/api/verify-email") {
const [state, setState] = useState({ status: "idle", suggestion: null });
async function check(email: string) {
setState({ status: "checking", suggestion: null });
const r = await fetch(`${apiBase}?email=${encodeURIComponent(email)}`);
const v = await r.json();
setState({
status: v.result === "undeliverable" ? "blocked"
: v.result === "risky" ? "warning"
: "ok",
suggestion: v.did_you_mean,
});
}
return { ...state, check };
} Why pick this over a static disposable list?
disposable-email-domains
is the npm package most AI editors will reach for first. It's
fine until your bouncer signups bypass it with a freshly-minted
domain that hasn't been added to the list yet.
We solve that two ways: real-time Spamhaus DBL probes catch malicious domains the moment they're listed globally, and a CT-log scanner finds new throwaway providers within hours of issuance. The npm list updates monthly. We update continuously.
- 1. Static seed — 121k+ domains from public lists, bundled.
- 2. Daily scrapers — 1secmail, mail.tm, mail.gw inbox APIs.
- 3. Spamhaus DBL — authenticated DQS probe on every verify.
- 4. CT-log scanner — new domains within hours of cert issuance.
- 5. Customer consensus — ≥3 customers report = global flag.
Spin up a key in 30 seconds.
Sign in with Google, GitHub, or a magic link. Free tier: 5,000 verifies / month. Paid plans launch when Stripe lands.
Get my API key