Stop Trial Abuse

Stop Serial Trials, Quota Farming & Perpetual Evaluators

Serial trialers change emails, cookies and IPs because those cost nothing; the laptop underneath usually stays the same. Send the new account's ID with one server-side check and the response counts how many of your accounts that device has opened. When Maskbreak recognises a fresh fake-browser profile, a script or a proxy, that shows up as a reason of its own.

1 callServer-side decision; timing on /status
FreePlan: 10,000 visitor checks a month — no card
1 fieldSend accountId to count your accounts per device
WatchFirst integration logs the decision and always continues

What you're up against

Trial resets that never end

New email, cleared cookies, fresh residential-proxy IP — and the free trial starts over. Nothing in a typical signup flow connects attempt #12 to attempt #11, so the reset loop runs for as long as the abuser cares to keep it going.

Quota farming on AI products

When the free tier includes model inference, every farmed account has a direct marginal cost. Scripted signups harvest quota across dozens of accounts, and the bill lands on your inference invoice, not the abuser's.

Competitors who evaluate forever

A rival's team can rotate trial accounts for months — benchmarking features, probing limits, demoing your product to their own prospects — without ever crossing the paywall or showing up as a single named account.

IP limits that proxies walk around

Per-IP signup caps and disposable-email blocklists are exactly the controls residential proxies and inbox aliases were built to defeat. The abuser adapts once, and the control stops working.

What Maskbreak does for you

  • Count how many of your accounts each device has opened: send accountId and read device.linked_accounts. A new email or a new IP does not reset it; a link expires after 90 days without a sighting
  • Read the network before you provision anything: a VPN alone comes back review, a recognised proxy (residential exits included) comes back block, and a cloud-server address alone is listed as datacenter_asn but allowed
  • Flag the fake browsers (antidetect), automation and emulators that make scripted trial farming cheap, when the device check sees them; all three block by default
  • Get an allow / review / block decision with reason codes, so trial policy stays in your code
  • Score every signup with one server-side call: no CAPTCHA, no extra step for honest evaluators
1 device

resetting the same free trial is the tell. A worked model of a B2B SaaS whose trials never converted, and the device-linking check that exposes the loop. Illustrative scenario, not a customer result.

Read the Playbook

Where the check goes

For trial abuse one call site matters most: the signup handler, after you create the account row and before the trial starts, so before a workspace, an API key or any quota exists. Pass that row's ID as accountId (set on your server, never read from the form) together with the email address, which is checked against known throwaway domains and not stored. If your product meters usage, run the same check wherever quota is granted or reset: that is the moment a farmed account starts costing you money. The example starts in watch mode: it logs each decision with linked_accounts, lets every signup through and fails open if Maskbreak is unreachable. After a few days, compare the log with the Events tab in your console, then turn on the enforce line and your own limit on linked_accounts; the integration guide has the full enforce checks.

// Trial signup: `user` is the account row you just created; no trial or quota yet
// 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,  // throwaway-domain check; the address is not stored
      accountId: String(user.id)  // your own id, server-side: counts your accounts per device
    })
  });
  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,
  linked_accounts: risk?.device?.linked_accounts ?? null,
  degraded: risk ? !!risk.degraded : null
}));
// Enforce later: if (risk?.decision === 'block') return res.status(403).json({ error: 'Signup rejected' });
// (disable the account row first). multi_account_device leaves the default decision alone,
// so repeat trials need your own limit: above it, start the trial only with a card on file.

The signals that matter here

The account count is the number that matters here. Serial trialers change everything that is free to change: email address, cookies, IP. A resetter who keeps the same machine and browser usually comes back as the same device.visitor_id, and when you send accountId, device.linked_accounts climbs with every new trial, whatever the form claims. The count covers your accounts only, stored as one-way hashes, and a link expires after 90 days without a sighting. (device.times_seen is not this count: it tallies sightings across all Maskbreak traffic.) That one number is the difference between stopping trial abuse at scale and playing whack-a-mole with email domains.

Network signals mean different things at signup. A commercial VPN on a paying customer's login is usually privacy hygiene; on a brand-new trial it is a reason to ask for one more step. By default a VPN alone comes back review, with the service named in network.service when known, and a recognised proxy, residential exits included, comes back block. A cloud-server address alone (datacenter_asn) stays allow, because engineers do evaluate from cloud dev boxes. The raw network block (network.vpn, network.proxy, network.datacenter, network.residential) is there when you want a stricter trial policy than your login policy.

Automation before the quota grant. Farming a free tier only pays if account creation is cheap, which in practice means scripts, emulators or antidetect browsers, fake browsers that hand out a new fingerprint per profile. When Maskbreak's device check sees them, the signup returns automation_detected, emulator_detected or antidetect_browser, all blocked by default, so once you enforce, the farmed account never receives an API key or a token of inference quota. A brand-new fake-browser profile has no earlier accounts to link to, which is why this check and the account count work as a pair.

A score and a decision, not a black box. Every check returns a risk_score from 0 to 100, a decision of allow, review or block, and the reasons behind it. That lets you run asymmetric policy: refuse automated signups, ask a VPN or a throwaway inbox for a verified work email, ask the fourth account from one laptop for a card on file, and keep a competitor's fifth anonymous trial on the shortest leash you offer. Keep the account limit in your own code. A Multi-accounting rule on the console's Rules tab matches every check that sends an accountId from a device linked to more than one of your accounts. It decides for that signal only, so a proxy, Tor, automation, fake-browser or emulator block on the same check stays a block.

What a repeat trial looks like in the response

The sixth trial from one laptop on an AI product, over home broadband, with an ordinary email address. Nothing about the visit itself is suspicious; the account count is the finding.

// Example response, selected fields: the 6th trial from one laptop
{
  "decision": "allow",
  "risk_score": 0,
  "reasons": ["multi_account_device"],
  "network": { "vpn": false, "proxy": false, "datacenter": false, "tor": false, "residential": true },
  "device": {
    "antidetect": false,
    "automation": false,
    "returning": true,
    "linked_accounts": 6,
    "multi_account": true
  }
}

The decision stays allow because multi_account_device alone never changes the default: families, agencies and shared office machines open second accounts too. Acting on linked_accounts: 6 is your call. Where every trial is inference you pay for, treat an allow whose reasons include multi_account_device as a review in your own handler and start trial six only with a card on file. Send a throwaway inbox from the same laptop and the answer becomes review with disposable_email; add a fake browser or a proxy and it becomes block.

Common questions

How does Maskbreak know trial #2 came from the same device as trial #1?
Send the new account's ID as accountId with the signup check. When the browser check completes, the response carries device.linked_accounts, the number of your accounts seen on that device, and adds multi_account_device once it passes one. The same machine and browser usually return the same device.visitor_id whatever the email or IP. A different browser or a fresh fake-browser profile starts a new count, which is why the fake-browser check matters too. Links are one-way hashes, counted within your account only and never across Maskbreak customers. device.times_seen is a different number: it counts sightings across all Maskbreak traffic, not your accounts.
What about legitimate users who open a second account?
It happens: a freelancer with two clients, a family on one laptop, someone who changed jobs. That is why multi_account_device does not change the decision by default. A second account from a clean home connection comes back allow; the same device behind a proxy, running automation or a fake browser comes back block because of those signals. Pick your own limit on device.linked_accounts in the signup handler, and above it hold the trial (a verified work email, a card on file) rather than refuse it.
Does this work for AI products where abuse burns inference quota?
Yes, and placement matters more there: run the check before you grant or reset quota, not after. A farmed account stopped at signup costs you one visitor check; one that gets through costs inference until someone notices the usage graph. If your product grants quota again later, such as a monthly reset or a referral credit, check there too with the same accountId.
Will the check add friction for honest evaluators?
Not on its own. There is no CAPTCHA and no challenge page: the browser snippet collects its evidence in the background, and your server gets the decision inside the signup request it already handles. Any friction is yours to add on review, for example a verified work email or a card on file before the features that cost you money, so a borderline signup gets one extra step instead of a refusal.
How do we roll this out without breaking real signups?
Start in watch mode: call the API on every signup, log the decision, the reasons and linked_accounts, and always let the signup continue. The Free plan includes 10,000 visitor checks a month with no card, so you can read a week of your own verdicts next to the Events tab before you enforce anything. Then set your limit on linked_accounts. A Multi-accounting rule on the Rules tab decides for the shared device only: a proxy, Tor, automation or fake-browser signal on the same signup still blocks.

End the perpetual free trial

Free plan: 10,000 visitor checks a month, no card. Paid plans from €29 a month. Counts your accounts per device and flags residential proxies, fake browsers and scripted signups when it recognises them.

Fraud BriefOnce a month · no spam · unsubscribe anytime
Get the new VPN, proxy & bot patterns we see each month
Short, technical breakdowns of what fraudsters changed last month — written for engineers, not marketers.
Stop fraud before it hides — try Maskbreak free. Free plan: 10,000 visitor checks a month, no card. Paid plans from €29 a month.