How to stop bonus abuse
Also called: promo abuse · signup bonus farming · welcome offer abuse · incentive abuse
What it costs you
- trending_downThe bonus itself, paid in cash or credit to somebody who was never going to become a user.
- trending_downThe acquisition budget behind it, because the campaign that looks like it converted at $4 a user actually converted at nothing.
- trending_downYour offer economics with partners, since a network seeing farmed conversions will eventually claw them back or drop the campaign.
- trending_downAnalyst hours, which is the cost nobody budgets for: somebody has to sit and untangle which of last week's 900 signups were real.
How the attack runs
- 01
Find an offer worth farming
The economics have to work for the farmer. A $5 signup bonus with a low completion bar is worth industrialising; a $5 bonus that requires a deposit and thirty days of activity is not. Abusers do this arithmetic before you do.
- 02
Solve identity supply
They need email addresses, and sometimes phones. Disposable domains, catch-all domains they own, and plus-addressing on a single inbox are the cheap end. SMS-receive services are the paid end, and they are cheap too.
- 03
Solve network supply
One address making forty signups is trivially caught, so traffic is spread across proxies. Datacenter ranges are the cheap option and the easy catch. Residential proxy networks route through real consumer connections and do not look like infrastructure, because they are not.
- 04
Run the accounts through the funnel
Often manually at first, to learn the exact minimum that triggers the payout, then repeated. The behaviour is deliberately unremarkable: each account does the least work that qualifies, and nothing else.
- 05
Consolidate the payout
This is the step that is hard to hide. Value has to end up somewhere the farmer controls, so many accounts converge on a small number of payout destinations, wallets, or withdrawal patterns.
What does not work
These are the defences most teams try first. They are listed here because trying them and watching them fail is expensive.
One account per email address
Email is free and effectively unlimited. Catch-all domains give one person infinite valid addresses, and plus-addressing gives them infinite variants of a single real inbox. This stops nobody who is trying.
One account per IP address
It blocks proxies you can already see, and it blocks legitimate users behind shared connections: student housing, offices, mobile carriers, whole countries on carrier-grade NAT. It costs you real users while the farmer buys another proxy for a dollar.
Blocking disposable email domains from a list
Worth doing, and nowhere near sufficient. Public lists lag new domains by weeks, and a farmer who owns a normal-looking domain never appears on one at all.
Requiring phone verification
Raises the cost per account, which is genuinely useful, and does not stop the economics. SMS-receive services sell verifications for cents. It converts an unlimited attack into a merely cheap one, and it costs you conversion on legitimate signups.
Reviewing it manually after the fact
It works, and it does not scale, and the money has usually left. Manual review is the right tool for the ambiguous middle, not for the whole funnel.
The signals that do
These are the signals that carry weight on bonus abuse, and why each one is the signal rather than the obvious alternative. Not all of them are ours: the ones marked you build this genuinely work and Kaidn does not check them, so you would be wiring them up yourself. Listing those unlabelled would read as a claim we cannot support.
Email identity rather than email validity
Kaidn checks thisA farmed address usually passes a syntax check and an MX lookup, because it is a real deliverable address. What separates it is the domain's age and history, whether it is disposable or catch-all, and whether the local part is a plus-addressed variant of an inbox you have already seen.
Velocity across the funnel, not just at signup
Kaidn checks thisFarms are rate-limited by human effort, so they cluster. Bursts of registrations, and more tellingly bursts of the same qualifying action, look nothing like organic arrival patterns.
ASN reputation, not just datacenter or not
Kaidn checks thisBinary proxy detection misses residential networks. What still separates them is that abuse concentrates in particular autonomous systems, and that concentration is measurable across your traffic over time.
Device and browser consistency
Kaidn checks thisFarmed sessions are frequently run from a small number of machines, and the tells are in the mismatches: a browser claiming one platform while its rendering stack says another, or automation traits in a session that should be manual.
Payout convergence
you build thisThe strongest single signal in reward fraud, and the most overlooked. Accounts that share nothing at signup often share a destination at withdrawal, and that link is only visible if you score the payout as well as the registration.
Bonus abuse is the fraud people underestimate because no single instance looks like fraud. Nobody stole a credit card. Nobody broke into an account. Somebody signed up, did the thing the offer asked for, and collected what you promised. Forty times.
That is what makes it hard: the individual account is genuinely unremarkable, and any rule strict enough to catch it in isolation will also catch a real user having a normal day.
The unit of detection is not the account#
The mistake almost everyone makes first is trying to decide whether this signup is legitimate, using only what this signup carries. Looked at alone, a farmed account has a working email, a plausible name, a real device, and an IP address belonging to somebody's house.
The abuse is not in the account. It is in the relationships between accounts, and those are only visible if you are holding the other ones. That is why a page of per-account validation rules never holds the line, and why the teams who beat this problem are the ones who started scoring the connections instead of the fields.
Detecting it, concretely#
Two calls, one at registration and one when value moves. The registration call is cheap insurance; the second is where a farm stops looking like forty independent people.
import { Kaidn } from "@kaidn/sdk"; const kaidn = new Kaidn({ apiKey: process.env.KAIDN_API_KEY }); const r = await kaidn.score({ event: "signup", user_id: user.id, ip: req.ip, email: user.email, device_id: body.kaidn_device_id, // from @kaidn/fp in the browser }); // Do not block a signup on this. Flag it, let them in, and hold the BONUS. if (r.verdict !== "allow") await flagForBonusHold(user.id, r.reasons);
The reasons worth routing on for this pattern, and what each one is actually saying:
| reason | check | what it means |
|---|---|---|
email_reuse | emailReuse | this mailbox is already behind other accounts, after aliasing is stripped |
aliased_address | emailRisk | dot or plus tricks that resolve to an inbox you have seen |
disposable_email | emailRisk | a known throwaway provider |
device_reuse | deviceReuse | this browser has carried other accounts |
abusive_asn | ipRisk | this autonomous system is over-represented in abuse across scored traffic |
device_velocity | velocity | this device is moving through the funnel faster than a person does |
Hold the bonus, not the account#
The block that costs you least is the one on the payout rather than the registration, because a false positive at signup loses a real customer and a false positive on a bonus loses a bonus:
const r = await kaidn.score({ event: "bonus_claim", user_id: user.id, ip: req.ip, email: user.email, device_id: body.kaidn_device_id, }); // device reuse on ONE network is corroborated; across three it is a household const corroborated = r.reasons.includes("device_reuse") && (r.device?.account_count_same_network ?? 0) > 1; if (r.verdict === "block" || corroborated) return holdBonus(user.id, r.event_id); if (r.verdict === "review") return queueForReview(user.id, r.reasons); return payBonus(user.id);
When the outcome eventually resolves, send it back. A confirmed farm three weeks later is the only ground truth the engine ever gets:
await kaidn.label({ event_id, label: "confirmed_fraud" });
Score the payout, not just the signup#
If you take one thing from this page: the registration is the wrong place to catch reward fraud on its own, because at registration the farmer has told you almost nothing yet.
By the time value moves, they have told you a great deal. They have told you which accounts qualify in the same unusual way, in the same window, through the same networks, and above all where the money is going. Payout convergence is the point at which forty independent-looking accounts stop looking independent.
Scoring both ends costs you a second call and changes what you can see entirely.
Decide what you actually want to happen#
Blocking is not always the right outcome, and treating this as a binary is how good users get hurt.
A large share of these cases are ambiguous: a real user on a shared connection, a returning customer on a new device, a genuine signup that happens to arrive in a burst because you ran an ad. Those belong in review, not in a block, and the difference between a fraud tool you can live with and one you cannot is usually whether it lets you set that threshold yourself.
Ours returns allow, review or block with the checks that fired and their weights, so you can see which signal pushed a user over the line and move that line if it is wrong.
What we would tell you to do first, before buying anything#
Look at your last thirty days of payouts and group them by destination. If a small number of destinations account for a disproportionate share of accounts, you have bonus abuse and you now know its size. That costs nothing and takes an afternoon, and it is a better basis for deciding what to spend than any vendor's estimate, including ours.
Frequently asked questions
What is bonus abuse?
Bonus abuse is the industrialised farming of a promotional offer: one person creates many accounts, each doing the minimum work the offer requires, and collects the bonus repeatedly. No credential is stolen and no account is broken into, which is what makes it hard: every individual account is genuinely unremarkable, and the abuse lives in the relationships between accounts rather than inside any one of them.
Why does one account per email address not stop it?
Because email is free and effectively unlimited. A catch-all domain gives one person infinite valid addresses, and plus-addressing plus dot-insertion gives them infinite variants of a single real Gmail inbox. The defence that works is not counting addresses but resolving them: strip the aliasing tricks to a canonical mailbox and count that instead, which is what the email_reuse check does.
Will requiring phone verification fix it?
It raises the cost per account, which is genuinely useful, and it does not change the economics. SMS-receive services sell verifications for cents, so it converts an unlimited attack into a merely cheap one, and it costs you conversion on legitimate signups. Treat it as friction that buys you time, not as a control.
Which signal actually catches a farm?
In practice, mailbox reuse after canonicalisation and device reuse corroborated by network, weighed together with arrival velocity. A single signal rarely convicts: device reuse across three different networks is a household or an office as often as it is a farm, which is why the response carries account_count_same_network alongside account_count.
Should I score the signup or the payout?
Both, and if you can only afford one call, score the payout. At registration the farmer has told you almost nothing. By the time value moves they have told you which accounts qualify in the same unusual way, in the same window, through the same networks. Kaidn scores the cashout event; correlating the destination those accounts withdraw to is a join you make on your own side, and it is worth making.
Score your own traffic for this
10,000 events a month free, no card. Every verdict comes back with the checks that fired and their weights, so you can see which signal caught it rather than trusting a number.