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.
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.
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.
Last reviewed June 17, 2026

