Orange textured background

Anti-fraud

How to stop referral-program fraud: self-referrals and referral rings

Referral rewards pay real money, so abusers refer themselves. Linking the referee's device to the referrer's is the check that holds when emails and IPs do not.

Referral programs pay real credit per signup, so the dominant abuse is self-referral and referral rings: one person or a handful of devices behind many 'distinct' referees. Cross-browser device matching links the referee back to the referrer and collapses the ring, even across browser switches and incognito.

How referral fraud actually pays out

Referral programs convert a signup into real value: account credit, cash, a free month, sometimes a withdrawable balance. That direct payout is what separates referral abuse from trial or coupon abuse. The reward is the target, not the product.

Two shapes dominate. Self-referral: one person signs up as their own referee through a second browser, an incognito window, or a fresh email, and collects both ends of the reward. Referral rings: a small set of physical devices cycles through many 'distinct' referee accounts to farm rewards at scale, sometimes coordinated across a group chat.

Both shapes share one structural fact. The referrer and the referee are the same device, or the ring's many referees trace back to a handful of devices. That is the thing to detect, and it is not something the account layer can tell you.

Why referral fraud slips past your existing checks

Most referral programs gate on the things the abuser controls. A unique email is free to create. A unique IP changes with a VPN or a mobile network. One-reward-per-cookie is cleared in a click and never written in incognito. None of these tie the referee back to the referrer, which is the relationship that defines the fraud.

A single per-browser fingerprint is closer but breaks at the abuser's first move. Self-referrers overwhelmingly use a second browser or an incognito window, and a per-browser hash reads Chrome and Firefox on the same laptop as two different devices. The exact check that should catch the self-referral is the one a single-hash library fails.

The check: link the referee to the referrer

The detection that holds is a pairwise comparison between the referee's device and the referrer's device, on hardware-bound signals only. `result.hardwareFingerprint` is derived from properties of the physical machine (GPU identity, CPU architecture, installed fonts, screen geometry, and related hardware-bound signals), so it is identical in Chrome, Safari, Firefox, and Brave on one machine, identical in incognito, and identical after a cookie clear. A self-referral through a second browser still matches the referrer.

`compareFingerprints` in `cross-browser` mode does this comparison. It weighs only hardware-bound signals, applies fuzzy matching to signals that drift legitimately, and returns a `matchScore` (0 to 100), a `match` verdict, and a `hardwareMatch` boolean. At attribution time you compare the new referee against the referrer who would be credited. For why hardware-bound and engine-bound signals separate this cleanly, see the companion post on hardware vs engine identity, and the full comparison API is documented at docs/api-compare-fingerprints.

import { getFingerprint, compareFingerprints } from 'doorman-benny';

// When a referee signs up via a referral link, compare their device
// against the referrer's stored device. A hardware match is self-referral.
async function assessReferral(
  referrerResult: object, // referrer's stored FingerprintResult
): Promise<{ selfReferral: boolean; matchScore: number }> {
  const refereeResult = await getFingerprint();

  // cross-browser mode: hardware-bound signals only. Catches the abuser
  // who refers themselves from a second browser or an incognito window.
  const cmp = compareFingerprints(
    referrerResult as any,
    refereeResult,
    { mode: 'cross-browser' }
  );

  return {
    selfReferral: cmp.hardwareMatch || cmp.match,
    matchScore: cmp.matchScore,
  };
}

Attribution-time check: a hardware match between referee and referrer is a self-referral, regardless of which browser or incognito window the referee used.

Two places to enforce: attribution and payout

Self-referral checks belong at two points, and the second matters more than teams expect. The first is attribution time: when a referee signs up through a referral link, compare them to the referrer before you attribute the reward. This catches the simple self-referral immediately.

The second is payout or redemption time, before referral credit converts into something withdrawable. Sophisticated abusers stagger signups so no single account trips a check, then assemble the ring later. Re-checking the whole referral chain at the moment value leaves the system catches rings that looked clean one account at a time.

import { compareFingerprints } from 'doorman-benny';

// Before releasing referral credit, check the referee against every other
// account already credited to the same referrer. A small device set behind
// many 'distinct' referees is a referral ring.
function countDevicesInChain(
  refereeResult: object,
  priorReferees: object[], // stored FingerprintResults for this referrer's chain
): { matchesInChain: number; isRing: boolean } {
  let matchesInChain = 0;

  for (const prior of priorReferees) {
    const cmp = compareFingerprints(
      prior as any,
      refereeResult as any,
      { mode: 'cross-browser' }
    );
    if (cmp.hardwareMatch || cmp.match) matchesInChain++;
  }

  // One device matching multiple 'different' referees is the tell.
  return { matchesInChain, isRing: matchesInChain > 0 };
}

Payout-time check: run the pairwise comparison across the referrer's whole chain so many 'distinct' referees collapse into a device count before any credit cashes out.

Catching the ring, not just the pair

A referral ring is not one match, it is a cluster: many referees collapsing onto a few devices. Running the pairwise comparison across the referrer's whole chain turns 'distinct' referees into a device count. A chain of twenty referees that resolves to three physical devices is not a referrer with twenty friends.

Keep this at the level of 'how many devices are behind these accounts', not per-signal internals. The device count and the per-pair `matchScore` are the operational outputs you act on; how the score is weighted is not something you need to reason about to make the call.

The end-to-end flow

StepWhat happens
1. CaptureAt signup (for both referrer and referee), call getFingerprint() client-side. You get fingerprint, hardwareFingerprint, and consistency. No server involved yet.
2. SendPOST the two hashes plus the consistency result to your backend over your existing API along with any referral code. Decide server-side: never trust a verdict computed in the browser.
3. StorePersist a record keyed by hardwareFingerprint: { hardwareFingerprint, fingerprint, accountId, firstSeen, lastSeen }. At referral-code creation time, also store the referrer's hardwareFingerprint on the referral record.
4. Look upAt reward claim, re-capture the referee's device and compare it against the stored referrer hardwareFingerprint. Use compareFingerprints in cross-browser mode for a matchScore that tolerates minor hardware drift.
5. DecideIf the referee's device matches the referrer's device (exact hash or high matchScore), void the reward. Layer in consistency.spoofLikelihood on both sides to catch anti-detect browsers generating fresh identities.
6. Act and logVoid, hold for review, or pay out. Log which signals contributed to the match for support review and update lastSeen on both device records.

Referral reward eligibility by device match

ConditionOutcome
No stored referrer fingerprint for this referral codeCannot verify device; flag for manual review before paying.
Referee hardwareFingerprint matches referrer hardwareFingerprint (exact)Same device: void the reward. Log for audit.
Referee hardwareFingerprint has high matchScore vs referrer in cross-browser modeProbable same device: hold for manual review before paying.
Referee or referrer has high spoofLikelihoodEvasion signal: hold reward pending identity verification.
No match and low spoofLikelihood on both sidesDifferent devices, no evasion signal: eligible to pay out.

Responses and false positives

A referrer-referee device match is a strong reason to hold the reward, not necessarily to ban an account. Households share devices: roommates, partners, and family on one computer can legitimately refer each other, and a shared office machine produces real referrals between coworkers. A hardware match between referrer and referee is suspicious for a payout, but it is not proof of fraud.

Two practices keep this fair. First, hold the reward rather than the account: on a match, pause the referral credit pending review instead of suspending the referee, so a legitimate household referral becomes a delayed reward and not a lockout. Second, pair the device match with `result.consistency.spoofLikelihood`. A match from an account running a 'high' spoof likelihood, an anti-detect browser minting fake referees, is a materially stronger signal than a match from two clean browsers in one home.

Frequently asked questions

What is the difference between referral fraud and ordinary multi-accounting?

Multi-accounting is one device behind many accounts. Referral fraud is the economic attack where those accounts are linked as referrer and referee to farm a per-signup reward. Device matching underlies both, but referral fraud adds the referrer-to-referee link and a second enforcement point at payout time. The duplicate-signups post covers the general multi-accounting case.

Can a self-referrer beat this by using incognito or a different browser?

No. Cross-browser hardware matching is built for exactly that move. The hardwareFingerprint is derived from hardware-bound signals that are identical across Chrome, Safari, Firefox, and Brave on the same machine and identical in incognito, so a self-referral through a second browser still matches the referrer's device.

What about legitimate referrals between people in the same household?

This is the main false positive. Household members on a shared computer produce a genuine hardware match between referrer and referee. The recommended response is to hold the reward for review rather than block the account, and to weigh the match alongside consistency.spoofLikelihood. Most legitimate household referrals clear review, while a match from an anti-detect browser does not.

Does catching referral fraud require a server?

Fingerprint collection runs client-side, and compareFingerprints is a synchronous function that needs no network call. You run the referrer and chain comparisons on your own backend against stored fingerprints; there is no external service or API key involved in the detection itself.

Where do I store the referrer's fingerprint?

On your referral record in your own datastore, alongside the referral code and the referrer's account ID. When you generate the referral link, capture the referrer's hardwareFingerprint client-side and store it on the referral record. At reward claim, you compare the incoming referee fingerprint against this stored value.

How is this different from using ThumbmarkJS for referral checks?

ThumbmarkJS returns a single per-browser hash with no built-in comparison API and no separate cross-browser hardware hash. You would build the chain comparison yourself and still miss the common self-referral through a second browser, because the same person in Chrome and Firefox produces different ThumbmarkJS hashes. Benny's cross-browser mode and hardwareFingerprint are designed for the referrer-to-referee link.

Get started

Stop paying out fraudulent referrals

npm install doorman-benny. Cross-browser hardware matching and a built-in comparison API in the free tier. Link referees to referrers and collapse referral rings before the credit cashes out.

Get startedcompareFingerprints API reference

Related posts

Last reviewed June 17, 2026