← All guides

How to choose a fraud score threshold

Anyone who answers this with a single number is guessing on your behalf. The score is the same in every flow; what changes is what a mistake costs. Wrongly blocking a newsletter signup costs you an email address. Wrongly blocking a password reset costs you a customer who cannot get into an account they already pay for. Those two cannot share a cutoff, and the reason people end up with a bad threshold is almost always that they picked one number and applied it everywhere.

  1. 1

    Work out the exchange rate before you pick a number

    Every threshold is a trade at some rate: how many real customers you will turn away to stop one fraudulent signup. You do not need a precise figure, but you do need to know which way it leans, because that decides everything downstream. Protecting a payout or a credit balance justifies refusing a lot of good traffic to stop one bad transaction. Protecting a free newsletter almost never does. Write the two costs down in whatever unit is honest for your business, and the rough shape of the threshold falls out of the comparison.

  2. 2

    Look at your own score distribution before you set a cutoff

    A threshold chosen before you have seen your traffic is a number pulled from someone else's business. Run scoring in log-only mode for a week or two — score every signup, act on none of it — then plot how the scores fall. Most products find the bulk of real traffic clustered low with a thin tail above it, and the useful cutoff is where that tail separates rather than at any round number. If your distribution has no separation at all, that is worth knowing before you start blocking on it, and no amount of threshold tuning will fix it.

    // Log-only: score everything, block nothing, decide later.
    const r = await score({ ip, email, phone });
    await log.signup({ userId, score: r.score, verdict: r.verdict });
    return createAccount();  // no branch on the score yet
  3. 3

    Use three bands, not two

    The instinct is one line: above it refuse, below it allow. That framing forces every borderline case into one of two wrong answers, and borderline is exactly where real customers and real attackers overlap. A middle band routes ambiguity to a step-up — an emailed code, an SMS, a card check — which is the only move that is cheap when you are right and recoverable when you are wrong. A real customer completes a one-time code in a few seconds. Widening the middle band is nearly always better than agonising over where a single line goes.

    const r = await score({ ip, email, phone });
    
    if (r.verdict === "block")  return reject();
    if (r.verdict === "review") return sendOneTimeCode();  // the band that saves you
    return createAccount();
  4. 4

    Set the level per flow, using one score

    Score once, then let each flow read the same number against its own cutoffs. LayerCall exposes this as a strictness level on the request rather than making you hard-code numbers: level 0 blocks at 85 and reviews at 55, level 1 — the default — blocks at 70 and reviews at 40, level 2 blocks at 55 and reviews at 30, level 3 blocks at 40 and reviews at 20. The score itself is untouched; only the two boundaries move. A sensible starting arrangement is the lenient end for anything a real customer does while already logged in or already known to you, the default for new signups, and the strict end reserved for the flows where money moves.

    # same score, different boundaries per flow
    curl "https://www.layercall.com/v1/score/ip?ip=1.2.3.4&strictness=0" \
      -H "X-Api-Key: YOUR_KEY"   # password reset — protect the existing customer
    
    curl "https://www.layercall.com/v1/score/ip?ip=1.2.3.4&strictness=2" \
      -H "X-Api-Key: YOUR_KEY"   # payout request — protect the money
  5. 5

    Measure both mistakes, or you will only ever tighten

    Fraud that gets through is loud: it arrives as a chargeback, a complaint, a cleanup job. A customer wrongly refused is silent — they close the tab, and nothing in your dashboard records that they existed. If the only feedback reaching you is the loud kind, every review will conclude the threshold is too loose, and it will ratchet in one direction for as long as the product lives. Counter it deliberately: count challenges issued and how many were completed successfully, because a step-up that real people pass at a high rate is a band doing its job, and one they abandon is a band set too wide. Feed confirmed outcomes back so the question stops being a matter of opinion.

    curl -X POST "https://www.layercall.com/v1/outcome" \
      -H "X-Api-Key: YOUR_KEY" -H "Content-Type: application/json" \
      -d '{ "request_id": "req_...", "outcome": "legitimate" }'
  6. 6

    Change it on evidence, and change one thing at a time

    The worst moment to redesign a threshold is the morning after an incident, when the only visible cost is the one that just happened. Move it deliberately instead: one level at a time, then watch both numbers — signups refused and challenges passed — for long enough to see a weekly cycle, since traffic on a Sunday does not look like traffic on a Tuesday. If you change the strictness level and the scoring inputs in the same week, you will not be able to attribute the result to either.

What this gets wrong

Every check here has a cost in real customers. These are the ones that actually bite — several because we shipped them ourselves.

  • Copying a number out of a vendor's documentation, including ours. The defaults are a reasonable place to start; they are not tuned to your traffic, and nobody else's can be.
  • Reading the score as a probability. It is an ordering, not a percentage — 80 is riskier than 40, but it does not mean an 80% chance of fraud, and treating it that way leads to arithmetic that quietly means nothing.
  • Running two bands because the middle one felt like indecision. The middle band is where the value is: it converts your uncertain cases into a question the real customer can answer.
  • One threshold for every endpoint. Signup, login, password reset and payout have different costs of failure, and a cutoff correct for one is wrong for the others by construction.
  • Tuning only after fraud gets through. False positives never file a complaint, so a threshold reviewed only in the wake of incidents moves in one direction forever.
  • Blocking on a single strong signal regardless of the total. A datacenter address alone is a VPN user, not an attacker — that is what the composite score is for.

Try it before you integrate

Paste any IP, email, phone or domain into the playground — no signup. Or get a free API key for 1,000 lookups a month, no card.