Disposable Email Detection Inside Your Signup Check
Maskbreak checks the signup email's domain against a community-maintained list of known burner domains that it downloads again every day. It is one optional field on the evaluate call you already make, checked in memory and never stored. A hit raises allow to review; it never blocks on its own.
What you're up against
New burner domains keep appearing
Disposable-mail services keep adding domains, because lists that block the old ones exist. The domain that soaks up your verification emails today may be too new for any list, ours included, until someone reports it.
Verification proves the inbox, not the intent
A burner mailbox receives your one-time code and clicks your link just fine; that is the whole product. Email verification confirms the mailbox works, not that a person intends to stay past the free tier or the first promo.
A copied blocklist goes stale
A domain list copied into your repo at build time catches a little less every week after the deploy, and updating it means a dependency bump, a review and a release, for data that changes all the time.
One human, fifty addresses
Email uniqueness is your weakest identity check. Anyone with a burner service can make as many "unique" identities as they like, so trial limits, promo caps and one-account rules quietly stop meaning anything.
What Maskbreak does for you
- Add disposable email detection with one optional field on the /v1/evaluate call you already make, at signup and at email change
- Check against a community-maintained list of several thousand burner domains that Maskbreak downloads again every day, with no dependency bump on your side
- A hit adds
disposable_email, adds 15 to the risk score and raises allow to review; it never blocks on its own, and a failed list download keeps the last good copy - The address is checked in memory and never stored or logged; only a true/false flag is kept
- Send your own account ID as well and
device.linked_accountsshows how many of your accounts the same device has been checked with, whatever address each one used
behind many new accounts within days of a signup bonus is the tell. A worked model of a fintech launch, the signals that catch it and the response plan. Illustrative scenario, not a customer result.
Read the PlaybookWhere the check goes
Disposable email detection is not a separate API. Pass the address the visitor typed as an optional email field on the same /v1/evaluate call that scores the device and network, at signup and when someone changes their email. The response gains email.disposable, and a hit adds the disposable_email reason, adds 15 to risk_score and raises an allow decision to review. The example starts in watch mode: it logs each verdict, lets every signup through and fails open if Maskbreak is unreachable. After a few days, compare the log with the Events in your dashboard, then uncomment the enforce line; the integration guide has the full enforce checks.
// One optional field adds the burner-email check to the verdict you already get // Watch mode first: log the verdict, always continue, fail open const body = req.body || {}; // sentinel.js adds monocle + sentinel_fp to your form let risk = null; try { const verdictRes = await fetch('https://maskbreak.com/v1/evaluate', { method: 'POST', signal: AbortSignal.timeout(5000), headers: { 'Authorization': 'Bearer ' + process.env.MASKBREAK_API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify({ token: body.token || body.monocle, // network evidence fingerprintEventId: body.fingerprintEventId || body.sentinel_fp, // device evidence tz: body.tz || body.sentinel_tz, email: body.email // optional — checked transiently, never stored }) }); if (verdictRes.ok) risk = await verdictRes.json(); } catch {} // unreachable or timed out: your handler still runs console.log('[maskbreak]', JSON.stringify({ decision: risk?.decision ?? null, reasons: risk?.reasons ?? null, risk_score: risk?.risk_score ?? null, degraded: risk ? !!risk.degraded : null, disposable_email: risk?.email?.disposable ?? null })); // Enforce later: if (risk?.decision === 'block') return res.status(403).end(); if (risk?.email?.disposable) return res.status(422).json({ error: 'Please use a permanent email address' });
Example response for a burner address on an otherwise ordinary home connection (excerpt; the device block is left out):
{
"decision": "review",
"risk_score": 15,
"reasons": ["disposable_email"],
"network": { "vpn": false, "proxy": false, "datacenter": false, "anonymous": false, "tor": false, "residential": true, "service": null },
"email": { "disposable": true }
}Don't take the docs' word for it: run the live check on your own visit on the homepage, or paste an IP into the free IP lookup tool (a bare address covers Tor exits and cloud ranges only). Neither takes an email address, so the burner-domain check, and multi-account linking, need the API with your key.
The signals that matter here
A list that updates itself. Maskbreak checks the domain against the community-maintained disposable-email-domains list (about 9,000 domains in September 2026) and downloads it again every day, matching the exact domain and its last two labels (so mail.burner.example is caught when burner.example is listed). New domains reach the list when someone reports them, so a burner domain registered this morning can pass until it is added. What you skip is the dependency bump and the release.
Review, never block, by default. The list can only add the disposable_email signal: 15 more risk points, and an allow becomes review. It never overrides the network or device checks and never blocks on its own. A failed or empty download is ignored and the last good copy stays in use. The Rules tab can give disposable email its own action; it covers the burner address only, so a proxy, Tor, bot or fake browser on the same signup still blocks (how rules combine).
The address is never stored. The email is compared in memory and then discarded; only the resulting true/false flag is kept on the evaluation record, never the address itself. That is a commitment in our privacy policy, not just a docs footnote, and it is why you can send the field without widening what you share.
The device behind fifty burner addresses. Every burner address looks unique; the device check underneath often does not change with it. Send your own account ID as accountId and device.linked_accounts counts how many of your accounts that device has been checked with, with multi_account_device in the reasons once it is more than one. That reason does not change the decision by default: act on the count in your own code (see multi-accounting). Linking is stored as one-way hashes and scoped to your account; it never crosses to another customer. device.times_seen is different: it counts every sighting of the device across Maskbreak, not your signups. A fresh profile in a fake browser can come back as a new device, but a fake browser that is detected blocks by default on the same call.
What it is not. This is a domain check, not mailbox verification or an email-reputation score. It will not tell you whether the inbox exists, how old the address is or whether it has been seen in fraud elsewhere. Keep your confirmation email.
Common questions
Catch burner emails without storing a single address
Free plan: 10,000 visitor checks a month, no card. Paid plans from €29 a month. One optional field adds the check to every signup.