Self-referrals, click bursts and the three checks that catch them
Fraud in small programs is boring: a partner buying through their own link, a script hitting a link, a traffic source that never converts. Three rules catch most of it.
Affiliate fraud detection for a small partner program does not need machine learning, a risk vendor or a data scientist. It needs three checks that run every night against the ledger and the click log: a self-referral check that compares the checkout email with the partner's own email after normalising Gmail dots and plus-aliases; a click-burst check that flags any link receiving more than a threshold of clicks in a short window; and a dead-traffic check that flags any link with many clicks from one user-agent string and no conversions over 30 days. Each check produces a flag the brand can see, and a flagged partner's payout is held until a person reviews it. Nothing is auto-rejected and nothing is silently paid. This post describes each check with the exact rule, the arithmetic of the thresholds, the false positives you should expect, what happens to a flagged partner, what the three checks do not catch, and the wording the terms need so that a flag is a rule rather than a surprise.
What fraud looks like in a small program
The fraud that reaches a program with fifty partners is not sophisticated. It is one of three things, and in our experience it is almost always the first.
- A partner buys your product through their own link, or has a colleague do it, to collect a discount and a commission on a purchase they were making anyway. The cost is one commission and the integrity of the ledger.
- A script or a paid click service hits a partner's link thousands of times. The purpose is to inflate click statistics, trigger a click-based bonus if you offer one, or make a traffic source look alive. The cost is misleading data and, if bonuses depend on clicks, money.
- A traffic source that produces clicks and never converts. Sometimes this is a script; often it is a broken embed, a bot that follows every link on a page, or a cheap traffic buy. The cost is the same as the second case.
Stolen cards and chargeback fraud do also happen, but they are caught by the payment layer: Stripe Radar scores the payment before it succeeds, and a dispute becomes a negative ledger row that reverses the commission [1][2]. The three checks here are about the partner layer, which the payment system cannot see.
Check one: self-referral
The rule is simple to state. For every commission row, take the checkout email from the Stripe customer and the partner's account email, normalise both, and if they are equal, do not attribute the sale and raise a flag. The work is in the normalisation, because the person doing this does not use exactly the same string.
Gmail ignores dots in the local part of an address, so jane.doe@gmail.com and janedoe@gmail.com deliver to the same inbox [3]. Gmail and most other providers also accept plus-aliases, so janedoe+promo@gmail.com is the same inbox again. Googlemail.com is an alias of gmail.com. A partner who signs up as jane.doe@gmail.com and buys as janedoe+test@googlemail.com has used the same address three different ways.
| Checkout email | Normalised | Partner email | Normalised | Match |
|---|---|---|---|---|
| jane.doe@gmail.com | janedoe@gmail.com | janedoe@gmail.com | janedoe@gmail.com | Yes |
| janedoe+promo@gmail.com | janedoe@gmail.com | jane.doe@gmail.com | janedoe@gmail.com | Yes |
| Jane.Doe@googlemail.com | janedoe@gmail.com | janedoe@gmail.com | janedoe@gmail.com | Yes |
| jane@janedoe.co | jane@janedoe.co | jane.doe@gmail.com | janedoe@gmail.com | No (different domain) |
| jane.doe@outlook.com | jane.doe@outlook.com | janedoe@outlook.com | janedoe@outlook.com | No (dots matter outside Gmail) |
Two refinements catch more without catching innocents. First, compare the domain of a custom-domain checkout email with the partner's website domain: a partner whose site is janedoe.co buying with billing@janedoe.co is a self-referral the email rule alone would miss. Second, do not apply the dot rule outside Gmail. Outlook, Yahoo and most corporate mail servers treat dots as significant, and normalising them would create false matches.
What the check does with a match matters as much as the match. RelayWonder removes the attribution (the sale is recorded as unattributed, so the brand's revenue figures stay right) and shows a flag on the partner. It does not ban the partner, because the first self-referral is very often a test: a new partner clicks their own link and buys a month to see the dashboard work. The terms should say that self-referred sales are not commissionable, so that the partner is not surprised and the brand is not negotiating.
Check two: click bursts
The rule: for each partner link, count clicks in a rolling 10-minute window. If any window exceeds 100 clicks, raise a flag. The threshold is deliberately generous. A real audience produces clicks spread over hours and days; a script produces them in seconds.
Here is the arithmetic of why 100 in 10 minutes is a safe line for most small programs. A YouTube video with 10,000 views in its first day and a 2% click rate on the description link produces 200 clicks across 24 hours, about 8 per hour, with a peak in the first few hours of perhaps 30 an hour. That is 5 clicks per 10 minutes at the peak. To hit 100 in 10 minutes from genuine viewers, the video would need roughly 20 times that peak, which is a video doing hundreds of thousands of views on its first day. A partner at that scale will have conversions and a varied set of browsers to show for it. A script that hits the link 1,000 times in 3 minutes shows neither.
The flag is not the verdict. When a burst is flagged, look at three things: the spread of user agents (a real surge from a viral post shows dozens of browser and device combinations; a script shows one or a handful), the IP spread (a real surge comes from many networks), and whether any conversions followed in the next 48 hours. A genuine viral moment passes all three and the flag is cleared in a minute. A burst with one user agent, one network and no sales is a script, and the clicks are excluded from any click-based statistic or bonus.
Check three: same user agent, no conversions
The rule: for each partner link, over a trailing 30 days, if more than 500 clicks share a single user-agent string and the link has zero conversions, raise a flag. This catches the slow version of the burst: traffic that arrives steadily, looks like volume, and never buys.
The user-agent header identifies the browser and operating system of the request [4]. Real audiences are diverse: an ordinary link that gets 500 clicks in a month will show well over a hundred distinct user-agent strings, because browser versions, operating systems and devices multiply. Five hundred clicks with one string is a program, a monitoring tool, a broken auto-refresh embed, or a bot that crawls every link on a page. None of those are customers.
The zero-conversion condition is what keeps this check honest. A partner could legitimately have an audience that skews heavily to one browser, but that audience still buys sometimes. If the link has even one conversion in 30 days, the check does not fire, and the single-string clicks are treated as noise rather than fraud. When it does fire, the action is the same as for a burst: the clicks are excluded from click statistics and bonuses, and the partner is told why.
Affiliate fraud detection as three nightly jobs
Each check is one query over data a partner program already has. They run once a night, after the day's invoice events have landed, and write flags to the partner record. Here is each check in one row.
| Check | Input | Rule | Default threshold | Action on match |
|---|---|---|---|---|
| Self-referral | Commission rows joined to partner accounts | Normalised checkout email equals normalised partner email, or custom domain equals partner site domain | Exact match | Remove attribution, flag partner, hold payout |
| Click burst | Click log with timestamps | Clicks in any rolling 10-minute window exceed the threshold | 100 clicks per 10 minutes | Flag partner, exclude clicks from stats and bonuses pending review |
| Dead traffic | Click log with user agents, conversion rows | More than N clicks share one user agent and conversions are zero over 30 days | 500 clicks, 0 conversions | Flag partner, exclude clicks from stats and bonuses pending review |
Running them nightly rather than in real time is a deliberate choice. A commission row is already held for 30 days before it can be paid, so a check that runs within 24 hours of the sale has 29 days of margin. Real-time checks add complexity and the only thing they would buy is a flag a few hours earlier on money that cannot move for a month anyway.
What happens to a flagged partner
In this affiliate fraud detection model, a flag does three things and nothing else. It appears on the partner's row in the brand console with the check that raised it and the evidence (the matched emails, the burst window, the user-agent count). It holds the partner's payout: their balance stays on the ledger and is excluded from the next batch until the flag is cleared. And it sends the partner a plain message saying which check fired and that their payout is paused pending review.
The ledger rows themselves are not touched, because the ledger is append-only. If a review concludes that a burst was a genuine viral moment, the brand clears the flag and the partner is included in the next batch with their full balance. If it concludes that the traffic was bought, the brand records the clicks as excluded and, if the terms allow, removes the partner. In neither case is history rewritten; the partner's statement in December still shows what happened in June.
Expect false positives, and plan the review to take under five minutes each. A new partner testing their own link is the most common self-referral flag. A newsletter with a large corporate readership on a single managed browser build can trip the user-agent check if it has a slow month. A product launch mentioned by a large account can trip the burst check with entirely real clicks. In our experience the majority of flags in a small program are one of these, which is why the default action is a hold and a message rather than a ban.
What these checks do not catch
Honesty about limits is part of affiliate fraud detection. The three checks do not catch:
- Cookie stuffing, where a partner's page drops your tracking cookie on visitors who never clicked anything, so that any later organic purchase is attributed to them. First-party cookies set only on a click to the brand's own domain make this harder, and invoice-level attribution lets a brand audit which sales a partner's cookie claimed, but detecting it requires looking at conversion rates per partner over time, not a nightly rule.
- Self-referral through an unrelated email. A partner who buys with a friend's email and a friend's card is invisible to the email check. The 30-day hold and the refund rule limit the damage, and a partner whose only sales share a billing address is worth a look, but no automatic rule catches it cleanly.
- Leaked coupon codes used by customers who were buying anyway. That is leakage, not fraud, and the terms should say how it is treated.
- Stolen cards. Those are the payment layer's job. Stripe Radar blocks or reviews them before the invoice is paid, and a successful chargeback later becomes a negative ledger row that reverses the commission automatically [1][2].
For programs that grow past a few hundred partners or that pay click-based bonuses, a fourth signal is worth adding: conversion rate per partner compared with the program median, reviewed monthly. A partner with ten times the median clicks and a tenth of the median conversion rate is buying traffic or stuffing cookies, and no single nightly rule says so.
The wording the terms need
A check that is not in the terms is an argument waiting to happen. Four sentences cover the three checks.
- Sales made using your own link or code, by you or by anyone acting on your behalf, are not commissionable. We compare the purchase email with your account email, including common aliases.
- We monitor click patterns. Links that receive automated, purchased or otherwise non-human traffic will have those clicks excluded from statistics and from any click-based payment.
- When one of these checks is triggered, your payout is paused until we have reviewed it. We will tell you which check was triggered and what we found.
- Repeated or deliberate violations may result in removal from the program and forfeiture of unpaid balances arising from the violation.
The important word in the third sentence is paused. Partners accept a hold they were warned about; they do not accept a silent non-payment. Every flag RelayWonder raises is visible to the partner as well as the brand, with the check named, for exactly this reason.
Why this is enough for most programs
The temptation is to treat affiliate fraud detection as a reason to delay launching, or to buy a risk product sized for a marketplace. For a brand with fifty to five hundred partners paying from its own PayPal or Wise account, the three checks, the 30-day hold, invoice-level attribution with refunds as negative rows, and a human review of flags before each batch cover nearly everything that actually occurs. The damage from the cases they miss is bounded by the hold and by the fact that the brand pays each batch itself after seeing the exceptions. That is the design: three queries, one flag, one pause, one person. It is what RelayWonder ships, and it is what we would build again.
FAQ
How does affiliate fraud detection handle a partner who legitimately buys the product?
The sale goes through normally; only the commission is not attributed. The self-referral check removes attribution and raises a flag, and the partner receives a message explaining that self-referred sales are not commissionable. Nothing is charged back and the partner is not removed for a single instance.
Why normalise Gmail dots and plus-aliases but not other providers?
Because Gmail documents that dots in the local part are ignored, so jane.doe and janedoe are the same inbox. Most other providers treat dots as significant. Plus-aliases are honoured by nearly all providers, so stripping the plus suffix is safe everywhere. Applying the dot rule outside Gmail would create false matches.
What if a real video goes viral and trips the click-burst threshold?
The flag is a request for review, not a penalty. A genuine surge shows many user agents, many networks and some conversions in the following days. The brand clears the flag and the partner is paid in the next batch. The hold costs the partner nothing except a short delay, and the message tells them why.
Can I change the thresholds?
Yes. The 100-clicks-per-10-minutes and 500-clicks-with-zero-conversions defaults suit programs with up to a few hundred partners. A brand with very large creators may raise the burst threshold; a brand paying click bonuses may lower the dead-traffic threshold. The self-referral check has no threshold; a match is a match.
Does a flag change the ledger?
No. The ledger is append-only. A flag holds the partner's balance out of the next batch and marks the relevant clicks as excluded from statistics. If the review clears the flag, the balance is paid in full. If it does not, the brand may remove the partner; the rows that were written remain for the record.
Sources
- Stripe Docs: Radar · Payment-level fraud scoring that runs before a charge succeeds.
- Stripe Docs: Disputes · How chargebacks are raised and resolved; the events that become negative ledger rows.
- Gmail Help: Dots do not matter in Gmail addresses · Confirms that dots in the local part of a Gmail address are ignored.
- MDN: User-Agent header · What the user-agent string contains and why it varies across real browsers and devices.
- Stripe Docs: Webhooks · Invoice paid, refund and dispute events that drive attribution and the nightly checks.
- Amazon Associates Operating Agreement · A widely read example of program terms that prohibit self-purchases and artificial traffic.