ARTICLE
You added disposable-email detection to your signup flow. Now the harder question shows up: when the check says an address is a burner, what should your product actually do with it?
The instinct is to hard-block. Sometimes that is right. Often it is not. Block too little and your free trial becomes a renewable resource for people who never plan to pay. Block too much and you turn away privacy-minded users, confuse honest people who made a typo, and push determined abusers toward Gmail accounts you cannot block at all.
This guide is for founders and backend engineers who already accept that a deliverable email is not the same as a trustworthy signup (we covered that in Free Trial Abuse: Why Email Validation Isn't Enough). Here we go one step further: how to turn a disposable-email result into a policy you can defend, tune, and explain to support.
Block disposable emails outright only when two things are true: the free thing you hand out has a real marginal cost, and the evidence is strong. For everything else, route the signup to a lighter-weight path such as email confirmation, reduced trial credits, or a manual look, instead of a dead end.
A workable setup has three lanes:
The rest of this article is about deciding which signups land in which lane.
A blanket block feels clean, but it has three predictable failure modes.
Forwarding and alias services exist for privacy, and mainstream products like Apple's Hide My Email generate random addresses that forward to a real inbox (Castle's research on disposable emails covers the difference between short-lived inboxes and persistent aliases). Some of those users are exactly the careful, technical buyers a developer tool wants.
Once a signup form blocks known disposable domains, low-effort abusers move to free provider accounts or domains they control. Castle makes the same point in the article linked above. A hard block on disposable domains removes the easiest path; it does not end the behavior.
A rejected signup leaves nothing to learn from. A reviewed signup leaves a record you can check a week later: did it confirm the email, did it activate, did it burn credits and vanish?
None of this means you should allow burners into a costly trial. It means the block should be earned by evidence, not triggered by a single category label.
"Disposable" is not one fact. A useful check tells you how sure it is.
Signup Risk API's disposable_email check returns one of five results: confirmed, likely, suspicious, none, or unknown. Those are very different inputs to a policy:
confirmed: the strongest evidence that the domain is a known disposable provider.likely / suspicious: weaker evidence than a confirmed match. Worth friction, rarely worth a hard block on its own.none: no disposable evidence found.unknown: the check could not be evaluated. That is not a pass and not a failure.That last point matters more than it looks. When a DNS lookup times out, Signup Risk API reports the evidence as unavailable, and the affected check contributes nothing to the score instead of inventing risk. Your policy should do the same: treat "we could not tell" as its own case, not as "clean" and not as "burner."
The disposable result also never stands alone. The same response includes other independent checks: domain infrastructure (DNS, MX, SPF, DMARC), provider type, role addresses like support@, domain typos like gmal.com, synthetic patterns like keyboard walks or numeric-only local parts, and name-to-email alignment when you send a name. IP infrastructure context (datacenter, VPN, proxy, Tor) is available on the Growth plan and above.
Rather than writing a rule for every combination, let the score combine the evidence and read the recommendation. Signup Risk API maps the 0–100 score to a band and an action:
| Risk score | Risk band | decision.action |
|---|---|---|
| 0–24 | low | allow |
| 25–49 | moderate | review |
| 50–74 | high | review |
| 75–100 | very_high | block |
Don't assume a confirmed disposable result always maps to block, or that it never does. Where it lands depends on the rest of the evidence in the request. For example, a very newly registered domain adds risk on top of disposable evidence, and IP infrastructure context adds more on Growth and above. So read decision.action together with checks.disposable_email.result. If your product wants every confirmed burner rejected, make that rule explicit in your own code, as the policy below does, rather than assuming the recommendation will do it for you.
Here is what a request and a response look like. The values below are illustrative, not a live result; real scores depend on current data and on everything else in the request.
curl https://api.signuprisk.com/v1/analyze-email \
-H "Authorization: Bearer $SIGNUPRISK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "maria.lopez@mailinator.com",
"name": "Maria Lopez",
"options": { "include_checks": true, "include_explanation": true }
}'Illustrative response, not a real API result:
{
"decision": {
"action": "review",
"risk_score": 70,
"risk_band": "high",
"summary": "High-risk signup. Primary concern: disposable email provider detected.",
"assessment": {
"confidence": "high",
"evidence_strength": "strong",
"evidence_coverage": "partial",
"uncertainty": "low",
"limitations": ["ip_not_supplied"]
}
},
"checks": {
"disposable_email": {
"result": "confirmed",
"is_disposable": true,
"confidence": "high",
"risk_contribution": 70
},
"domain_typo": { "result": "no_typo", "risk_contribution": 0 },
"role_address": { "result": "personal_like", "risk_contribution": 0 }
},
"explanation": {
"top_reasons": [
{
"reason": "Disposable email provider detected",
"check": "disposable_email",
"contribution": 70
}
],
"recommendation_reason": "Review this signup before granting normal account access."
}
}The response is trimmed for readability; with include_checks on, all eight checks are returned. Three fields do most of the policy work: decision.action (the recommendation), checks.disposable_email.result (what the check found), and checks.disposable_email.confidence (how sure the check is about that finding). The stricter rule in the policy below keys on the last two. Don't confuse that with decision.assessment.confidence, which describes how stable the overall recommendation is given the evidence that was available.
This is a starting point for a SaaS product with a free trial or free credits. Adjust the lanes to what your trial actually costs you.
async function scoreSignup({ email, name }) {
const res = await fetch("https://api.signuprisk.com/v1/analyze-email", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SIGNUPRISK_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ email, name, options: { include_checks: true } }),
});
if (!res.ok) return null; // timeout, quota, or outage: handled below
return res.json();
}
function trialAccess(result) {
// Scoring unavailable: don't hand out the expensive part blind.
if (!result) return { lane: "review", credits: "starter" };
const { action } = result.decision;
const disposable = result.checks?.disposable_email?.result;
if (action === "block") return { lane: "block" };
// Optional stricter rule: reject confirmed burners when the disposable check itself reports high confidence.
if (disposable === "confirmed" && result.checks?.disposable_email?.confidence === "high") {
return { lane: "block", message: "Please sign up with a permanent email address." };
}
if (action === "review") return { lane: "review", credits: "starter" };
return { lane: "allow", credits: "full" };
}A few choices in there are deliberate.
Decide your failure mode on purpose. Some disposable-check guides tell you to fail open: if the API errors, let the signup through. That is right for a newsletter form. For a trial that hands out GPU time or API credits, failing open means an outage is a free-credit window. The middle path above creates the account but holds back the expensive part until you can score it again.
Make "review" mean something concrete. Signup Risk API recommends; it never blocks or challenges anyone itself. Your app decides what review means. Good options, roughly in order of friction:
Stripe's guidance on multi-account abuse lands in the same place: rather than blocking at signup for higher-risk signups, gate the high-value action (trial activation, promo redemption, referral payouts) until the account shows baseline legitimate behavior (Stripe: How to detect fake users and multiaccount sign-up abuse).
Write a block message a human can act on. "Invalid email" makes an honest user think your form is broken. "Please sign up with a permanent email address" tells them exactly what to do.
Request explanations only when you need them. include_checks gives your code the structured evidence. Explanations draw on a separate monthly allowance (50 per month on Free), so ask for include_explanation in your review tooling or logs, not necessarily on every signup.
Once burners stop working, the cheapest next move for a repeat abuser is the same Gmail inbox with a +tag or extra dots. The email object in a Signup Risk API response includes a canonical field that folds those variants together (for example, stripping plus-addressing and Gmail dots, and folding googlemail.com into gmail.com). Store it and use it as your one-trial-per-identity key. It is not a delivery address; keep sending mail to what the user typed.
Honest limits, so you can plan around them:
Before you tighten anything, run a week in log-only mode: score every signup, store the action, disposable result, disposable confidence, and canonical email, and change nothing. Then check:
Once those answers look sane, turn the lanes on. Revisit when your trial gets more valuable, because a more expensive trial justifies a stricter policy.
For the full breakdown of what each check evaluates, see Email Validation vs Signup Risk Scoring. For trial-specific patterns, see SaaS trial abuse. To wire it up step by step, start with the quickstart.
The fastest way to settle your policy is to score real addresses from your own signup log and see where they land. Signup Risk API's Free plan includes the complete core signup-risk analysis: 200 requests per month, no credit card required.
and score your first signup in a few minutes.