Signup abuse signals: what to check before you create an account
Short answer. Check four groups before you create the account: automation (headless browser, scripted input, filled decoy field), repeats (same device, browser or mailbox as earlier accounts), email quality (disposable, newly registered or alias domain) and network (many signups from one network in a day). No single signal is proof; combine them into a graded decision.
Last updated 2026-10-26 ยท Written by the Nolgard team
The signals
| Signal | Group | Nolgard field or reason | How much to trust it | Common false positive |
|---|---|---|---|---|
| Headless or automated browser | Automation | Browser check token | Strong alone | Real users with privacy extensions can look unusual. |
| Filled decoy field | Automation | Honeypot | Medium; enforced at most as challenge | Agents that skip off-screen inputs avoid it. |
| Fourth account on one device in 30 days | Repeat | repeat_device | Strong for trials | Offices and families share devices. |
| Email alias of an existing account | Repeat | repeat.emailAliases | Strong | Some people use plus tags for filtering, honestly. |
| Disposable email domain | email.disposable | Medium | Privacy-minded users use relay services. | |
| Newly registered email domain | Domain age and MX | Medium | New companies have new domains. | |
| Ten accounts from one network in 24 hours | Network | repeat_network | Weak to medium; never joins clusters | Universities, carriers and offices. |
| Burst of signups on one email domain | Domain burst | Medium | A team onboarding together. |
Thresholds are Nolgard defaults from repeat accounts; you can tune them per site.
Steps
- Collect the browser token at submit with
client.js, and pass it, the request IP and the account to/v1/verify.// Your signup handler, after the browser sent `token` const res = await fetch('https://nolgard.com/v1/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ secret: process.env.NOLGARD_SECRET, token, remoteIp, action: 'signup', account: { id: newUserId, email } }), signal: AbortSignal.timeout(3000) }); const result = await res.json(); if (!result.success) return createAccount({ share: 0.2 }); // fail open, reduced grant (see /docs/#failures) if (result.decision === 'allow' || result.decision === 'limit') return createAccount({ credits: result.grant.credits ?? result.grant.share }); if (result.decision === 'challenge') return askForEmailConfirmation(); return reject(); - Read the reasons, not only the score. The response carries
scores.automation,scores.repeat,scores.abuseand reason ids, so you can explain each decision to support or to the user. - Map signals to a graded response. Automation goes to
blockorshadow, repeats tolimitwith a smaller grant, doubtful ones tochallengewith your own email confirmation. - Keep checking after signup. An account that passed can still burn credits. Send usage events and set spend caps.
- Feed back labels.
POST /v1/feedbackwith Fraud or Legit so your next decisions use your own ground truth.
Questions
Which single signal catches the most abuse?
It depends on the product. For free trials it is usually repeats on the same device or mailbox; for contact forms it is automation. Run Observe mode and read your own events.
Should IP reputation decide a signup?
Not alone. Many honest people share one address behind a carrier or office. Nolgard counts network accounts but never joins accounts into a cluster by IP prefix.
Are disposable emails always abuse?
No. Some people use relay addresses for privacy. Treat a disposable domain as a reason for a smaller grant or email confirmation, not a block.
How do I explain a decision to a user who complains?
Use the reason ids in the event and the appeals flow. Label the account Legit if you were wrong.
What about AI agents signing up?
Verified agents are identified by Web Bot Auth signatures or the operator's official IP ranges, not the user agent string. See AI agents.