Conure API

Next.js bot detection and email validation

A Route Handler that proxies the check server-side, plus optional Edge middleware for blocking at the door.

1. Install

npm install zod

2. Add the file

app/api/email-risk/route.ts

import { NextResponse } from "next/server";
import { z } from "zod";

export const runtime = "edge";

const Body = z.object({ email: z.string().email().max(254) });
const NEUTRAL = { is_risky: false, risk_score: 0, reasons: [] };

export async function POST(request: Request) {
  const parsed = Body.safeParse(await request.json().catch(() => null));
  if (!parsed.success) {
    return NextResponse.json({ error: "Invalid email" }, { status: 400 });
  }

  const key = process.env.CONURE_API_KEY;
  // A misconfiguration must not look like a risk verdict.
  if (!key) return NextResponse.json(NEUTRAL);

  try {
    const response = await fetch("https://conureapi.com/v1/email-check", {
      method: "POST",
      headers: { Authorization: "Bearer " + key, "Content-Type": "application/json" },
      body: JSON.stringify({ email: parsed.data.email }),
      signal: AbortSignal.timeout(3000),
    });
    if (!response.ok) throw new Error("conure " + response.status);
    return NextResponse.json(await response.json());
  } catch {
    return NextResponse.json(NEUTRAL);
  }
}

3. Wire it up

// middleware.ts - block datacenter IPs before rendering
import { NextResponse, type NextRequest } from "next/server";

export const config = { matcher: ["/signup", "/api/checkout"] };

export async function middleware(request: NextRequest) {
  const ip = request.headers.get("cf-connecting-ip") ?? request.headers.get("x-real-ip");
  if (!ip || !process.env.CONURE_API_KEY) return NextResponse.next();

  try {
    const response = await fetch("https://conureapi.com/v1/bot-check", {
      method: "POST",
      headers: {
        Authorization: "Bearer " + process.env.CONURE_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ ip, user_agent: request.headers.get("user-agent") ?? "" }),
      signal: AbortSignal.timeout(2000),
    });
    const verdict = await response.json();
    if (verdict.is_bot) return new NextResponse("Forbidden", { status: 403 });
  } catch {
    // fail open
  }
  return NextResponse.next();
}

4. Verify

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

Notes

Other frameworks