Which signup fields should Siftfy classify?
Classify free-text fields such as display name, bio, company, referral notes, or onboarding answers. Skip email addresses, IDs, and structured fields because the model is tuned for natural-language text.
use case · signups
Updated May 12, 2026
Bot signups are noisy in your dashboards, expensive on your activation funnel, and occasionally a genuine compromise vector. A Siftfy call inside your signup handler scores the free-text fields a real applicant fills out — display name, bio, company, "how did you hear about us" — and routes obvious junk to review without adding friction for legitimate users.
Siftfy is trained on natural language, not addresses. Don't pass the email address through the classifier — it'll either score zero or behave unpredictably. The good signal is in the optional copy fields: a "company" field that says "Need to clear my credit card debt FAST!" or a "bio" with three URLs is what the model is built to catch.
If your signup is email-only with no free-text fields, signups isn't the right place to put Siftfy — score the user's first piece of text content instead (their first comment, message, listing, etc.).
The example assembles every optional free-text field into a single classifier input. Two thresholds: 0.70 to hold for human review (genuine signups occasionally score here, especially if they have unusual marketing copy in their bio), and 0.95 to silently shadow-ban (genuine signups effectively never score this high — almost certainly a bot).
// Next.js App Router signup handler. The interesting work is
// constructing the classifier input from whichever free-text fields the
// user filled in — display name, bio, company, "how did you hear about us".
// Skip the email address and other structured fields.
import { NextResponse } from "next/server";
const SIFTFY_KEY = process.env.SIFTFY_KEY!;
const HOLD_THRESHOLD = 0.70; // hold for review above this
const REJECT_THRESHOLD = 0.95; // hard reject — genuine signups rarely score this high
type SignupInput = {
email: string;
displayName?: string;
bio?: string;
company?: string;
referrer?: string;
};
export async function POST(req: Request) {
const body: SignupInput = await req.json();
const { email } = body;
// Concatenate every free-text field into a single classifier input.
// Empty fields are fine — the model is robust to short inputs.
const text = [body.displayName, body.bio, body.company, body.referrer]
.filter(Boolean)
.join("\n")
.trim();
let probability = 0;
if (text) {
try {
const resp = await fetch("https://api.siftfy.io/v1/predict", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": SIFTFY_KEY,
},
body: JSON.stringify({ text }),
signal: AbortSignal.timeout(2000),
});
if (resp.ok) {
({ spam_probability: probability } = await resp.json());
}
} catch (err) {
// Fall open: never block a paying customer because Siftfy was slow.
console.error("siftfy failed", err);
}
}
if (probability >= REJECT_THRESHOLD) {
// Don't return an error — that just informs the bot. Pretend success
// and create the account in a "shadow" state.
await createShadowAccount({ email, probability });
return NextResponse.json({ ok: true });
}
const status = probability >= HOLD_THRESHOLD ? "needs_review" : "active";
await createAccount({ ...body, status, spamProbability: probability });
return NextResponse.json({ ok: true });
}Bots don't only spam your text fields — they signal in other ways. Combine Siftfy's score with:
The calibrated 0–1 output from Siftfy makes it easy to combine into a single risk score weighted alongside these.
Classify free-text fields such as display name, bio, company, referral notes, or onboarding answers. Skip email addresses, IDs, and structured fields because the model is tuned for natural-language text.
Use a review state for borderline scores and reserve silent rejection for very high probabilities. That keeps real users from being blocked by unusual but legitimate profile text.
Do not force a signup check. Classify the user's first meaningful content instead, such as a comment, listing, message, or profile update.
See /v1/predict for the full reference, or related patterns: fake registrations (webhook pattern), contact forms, comments, SMS.