All articles Fundamentals

Handling Appeals When a Real User Is Flagged as a Repeat Trial

Every device limit eventually blocks someone who did nothing wrong. A designer who made a trial at their last job and now wants one at their new one. A parent setting up an account on the laptop their teenager already used. A shared machine in a coworking space. The limit was right on average and wrong for that person, and how you handle the next five minutes decides whether they become a customer or a one-star review.

Most teams put all their effort into the detection side and leave the appeal path as an afterthought: a generic error and a support inbox. This post is about designing that path on purpose.

Start with the message

The blocked person reads one sentence. It should do three things: say what happened in plain terms, avoid any accusation, and offer a next step on the same screen.

A message that works:

We couldn’t start a new trial from this device. If you’re new here, verify your phone number to continue, or contact support and we’ll sort it out.

A message that doesn’t:

Suspicious activity detected. Your signup has been blocked.

The first describes the device. The second describes the person and gives them nowhere to go. Also avoid naming the exact rule (“this device already has 3 accounts”). It doesn’t help a legitimate user, and it tells a farmer exactly what threshold to stay under.

Replace dead ends with a verify path

The strongest appeal process is the one most people never need, because the block wasn’t a block. Prynt’s signup recipes support four actions: allow, flag, verify and block. Most over-limit cases fit verify better than block:

  • verify creates the account but holds back the trial, credits or API keys until the user completes a step that costs a farmer more than it costs a real person. That might be a phone number not seen on other accounts, a card authorization, or SSO with a work domain.
  • block refuses outright. Keep it for unambiguous cases: Prynt’s own block decision (automation, tampering, a device confirmed bad), a truncated accountsOnDevice list, or a device you have already denylisted.

With a verify step in place, the false positive that would have been a support ticket becomes thirty seconds of friction. Degraded free tiers goes deeper on what to hold back and how to unlock it.

What support needs to see

When a ticket does arrive, the agent shouldn’t have to guess. Store the evaluation with every signup attempt, including refused ones:

{
  "attemptId": "su_4821",
  "email": "[email protected]",
  "requestId": "req_…",
  "visitorId": "vst_…",
  "action": "block",
  "reasons": ["device_account_limit"],
  "decision": "allow",
  "otherAccounts": [
    { "linkedId": "user_3381", "events": 41, "firstSeenAt": "2025-03-02T…", "lastSeenAt": "2025-11-19T…" }
  ]
}

That record answers the agent’s real question: who else is on this device, and does it look like one person farming or several people sharing? One long-lived account with plenty of activity, last used a year ago, is almost certainly a previous job or a family member. Six accounts with one event each, created minutes apart, is not. The accountsOnDevice list gives you exactly this view; accountsOnDevice explained walks through reading it.

Write a short decision guide for agents with two or three patterns and the action for each. Without it, every agent invents their own policy, and your false positive rate starts depending on who is on shift.

Overrides: scope them tightly

Once support decides the user is legitimate, you need to let them through, and it’s easy to let through far more than you meant to.

Know what a console allowlist does. Prynt’s allow and deny lists match on visitorId, linkedId, IP or country, and they run before any rule. An allowlist hit returns allow with the ALLOWLIST reason code and skips everything else, bot and tampering checks included. A deny entry always beats an allow entry. That makes an allowlist a big hammer: an allowlisted visitorId is trusted even if the device is later driven by a script.

Know what it doesn’t do. If your signup policy counts accountsOnDevice itself, which the ready-made recipes do, an allowlist changes Prynt’s decision but not the count. The device still has three accounts on it, and your code will still refuse the fourth. You need your own override.

A minimal override table keyed by device, with an expiry and a reason:

CREATE TABLE signup_overrides (
  visitor_id   TEXT PRIMARY KEY,
  granted_by   TEXT NOT NULL,
  reason       TEXT NOT NULL,
  extra_slots  INT  NOT NULL DEFAULT 1,
  expires_at   TIMESTAMPTZ NOT NULL
);
const override = await db.signupOverrides.find(event.visitorId);
const limit = baseLimit + (override && override.expiresAt > Date.now() ? override.extraSlots : 0);
if (otherAccounts >= limit) return verifyOrBlock();

extra_slots grants one more account, not unlimited ones, and expires_at stops a one-time exception from becoming a permanent hole. If the block happened at signup there is no account yet, so the override has to key on visitorId. Once the account exists, prefer overrides keyed on your own user id, as in one account per person enforcement.

Label the outcome

An override fixes one ticket. A label stops the next one. Report the resolution back to Prynt with the outcomes endpoint:

curl -X POST https://api.pryntid.com/v1/outcomes \
  -H "Authorization: Bearer $PRYNT_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"requestId":"req_…","label":"legit","notes":"Previous employer trial, verified by support"}'

legit and trusted record a good outcome. fraud, abuse, bot, spam, chargeback and account_takeover record a bad one. The same works the other way: if an appeal turns out to be a farmer with a good story, label it abuse so the device is flagged on future visits. Labels feed Prynt’s reputation data and give you a clean dataset for tuning. Console cases do the same without an API call, which suits teams working from a review queue.

Close the loop on the rule itself

Appeals are your best source of false positive data, because each one is a confirmed mistake with a reason attached. Once a month, group resolved appeals by the reason that triggered them:

  • If most granted appeals come from device_account_limit with old, active prior accounts, your limit ignores recency. Count only accounts seen in the last few months.
  • If they cluster on one customer type, such as agencies, schools or shared offices, that segment needs its own limit or an SSO path.
  • If almost none are granted, your messaging may be scaring people away from appealing at all. Check how many blocked users never contact you.

Reducing false positives covers the measurement side in detail.

Draft the agent decision guide and the override table before you enforce your first device limit, not after the first angry ticket. It takes an afternoon, and it means your detection can be strict without making real users pay for it.

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