All articles Fundamentals

Flag, Don't Block: Degraded Free Tiers for Suspicious Signups

A signup check that can only say yes or no forces a bad trade. Set it strict and you refuse real people who happen to share a laptop or work behind a corporate VPN. Set it loose and trial farmers walk through. Most of the risk sits in a gray middle where the honest answer is “probably fine, not sure.”

The fix is a third outcome. Create the account, but give it a smaller, slower version of the free tier until the user earns the rest. A farmer opening their eighth account gets something nearly worthless. A real person who was misjudged gets a product they can still evaluate, and a clear way to unlock the full tier.

Where the flag outcome comes from

Prynt’s decision has three values, allow, challenge and block, and the signup recipes for Express, Clerk, Supabase and Auth0 map them, together with the device’s account history, onto four actions:

  • allow: full free tier.
  • flag: full free tier, marked for review. Nothing changes for the user.
  • verify: account created, entitlement held back until a step is completed.
  • block: no account.

By default, Prynt’s challenge maps to flag, a missing identification maps to flag, and a device already holding two other accounts maps to block. Moving that last one from block to verify is the single biggest change most products can make. It’s a configuration value, not a rewrite:

PRYNT_MAX_ACCOUNTS_PER_DEVICE=2
PRYNT_ON_LIMIT=verify        # was: block
PRYNT_ON_CHALLENGE=verify    # was: flag
PRYNT_ON_PRYNT_BLOCK=block   # automation, tampering: still refused

Your app decides what verify means. That’s the product design work this post is about.

Five ways to degrade

Pick the ones that match what farmers actually take from you.

1. Reduced credits

If your free tier is metered (AI generations, API calls, render minutes), grant a fraction of it up front and the rest after verification. A farmer multiplying accounts to stack credits sees each account’s value drop sharply. A real user still gets enough to see whether the product works.

2. Delayed API access

Abusers rarely want your UI. They want API keys to wire into a script. Hold key creation for unverified accounts, or issue keys with a low rate limit. People evaluating the product in the dashboard don’t notice. Someone farming keys gets nothing until they verify.

3. No outbound reach

Free tiers that send email, publish pages, create share links or invite teammates are spam infrastructure waiting to happen. Turn those features off until verification. This one costs almost nothing in conversion, because few people send outbound messages in their first ten minutes.

4. Verify to unlock

The verification step should cost a farmer more than a real user:

  • Phone number not already attached to another account. Cheap for a person, costly at scale.
  • Card authorization with no charge. Strong, but it adds real friction, so keep it for the strongest signals.
  • Work SSO for B2B products. A company identity is hard to farm.
  • A proof-of-work challenge through prynt.challenge(), which returns { passed, passToken } for your server to validate. It slows scripts without asking humans to do anything.

5. Cooldown

Make the first session work fully, and only gate the second account from the same device for a period. It suits consumer products where a household legitimately shares one machine but nobody needs three new accounts in a day.

Wiring it into entitlements

Keep the verdict and the entitlement separate. Store the verdict on the user at signup, and derive entitlements from it plus the user’s verification state:

const event = await prynt.getEvent(req.body.pryntRequestId);
const verdict = evaluateSignup(event, { policy });   // allow | flag | verify | block

if (verdict.action === 'block') return res.status(403).json({ error: USER_MESSAGE });

const user = await users.create({
  email: req.body.email,
  signupVerdict: verdict.action,
  signupReasons: verdict.reasons.map((r) => r.code),
});
await prynt.updateEvent(req.body.pryntRequestId, { linkedId: String(user.id) });
function entitlements(user) {
  const trusted = user.signupVerdict !== 'verify' || user.verifiedAt;
  return {
    monthlyCredits: trusted ? FREE_CREDITS : Math.floor(FREE_CREDITS / 10),
    apiKeys:        trusted,
    outboundEmail:  trusted,
    shareLinks:     trusted,
  };
}

Two practical notes. First, link the account (updateEvent with linkedId) even for degraded signups. Otherwise the next account from that device won’t see this one. Second, never show the verdict to the user. They see “verify your phone to unlock API access,” not “you were flagged.”

Write upgrade copy, not suspicion copy

Present the degraded tier as a normal step, because for most people it is one:

Your workspace is ready. Verify your phone number to unlock API keys and your full monthly credits.

This reads like standard onboarding, so a false positive feels no friction they wouldn’t expect from any SaaS product. Avoid “for security reasons” and anything suggesting the person did something wrong. If someone can’t or won’t verify, give them a support path. Handling appeals covers that side.

Measure both sides

A degraded tier only works if it actually separates the two populations. Track per verdict:

  • Verification completion rate. Real users in the verify bucket should complete at a reasonable rate. If almost nobody completes, the step is too heavy or the bucket is mostly farmers. Look at their accountsOnDevice history to tell which.
  • Conversion to paid for verified-from-verify users versus allow users. If they convert similarly, your thresholds are catching real customers and should loosen.
  • Resource consumption for unverified accounts. This is the number that tells you farming stopped paying off.

Change one threshold at a time and compare cohorts, as in A/B testing risk rules and tuning fraud thresholds.

When to block anyway

Degrading doesn’t replace blocking. Refuse outright when the evidence is unambiguous: Prynt returns block for automation, tampering or a device confirmed bad through the reputation network; the accountsOnDevice list is truncated, meaning dozens of accounts; or a denylisted device returns. Everything in between is a candidate for a smaller free tier rather than no tier.

If you’re starting from a binary check today, switch your device-limit action from block to verify, hold back the one feature farmers value most, and watch the numbers for two weeks. Protecting free plans from abuse and protecting the signup funnel without killing conversion cover the rest of the playbook.

Try it free

Prynt is device intelligence with a free tier — visitor IDs, bot & fraud Smart Signals, and behavioral biometrics, powered by a cross-site network. Start free.

Keep reading