Fraud playbook

How to stop bonus abuse

Alex MugoFounder, Kaidn
3 min readRevised

Also called: promo abuse · signup bonus farming · welcome offer abuse · incentive abuse

How bonus abuse works, why per-account limits never hold it, and which signals separate a farmed signup from a real new user before the payout clears.

What it costs you

  • trending_downThe bonus itself, paid in cash or credit to somebody who was never going to become a customer.
  • trending_downThe ad budget behind it. The campaign that looks like it brought users in at $4 each actually brought in nothing.
  • trending_downYour standing with partners. A network that sees farmed conversions will claw them back or drop the campaign.
  • trending_downAnalyst time, which nobody budgets for. Somebody has to sit down and work out which of last week's 900 signups were real.

How the attack runs

  1. 01

    Find an offer worth farming

    The maths has to work for the farmer. A $5 bonus that pays out easily is worth doing at scale. A $5 bonus that needs a deposit and thirty days of activity is not. They do this arithmetic before you do.

  2. 02

    Get a supply of emails

    They need addresses, and sometimes phone numbers. Throwaway providers, a catch-all domain they own, and plus tags on one inbox are the cheap end. Services that rent you an SMS code are the paid end, and those are cheap too.

  3. 03

    Get a supply of IP addresses

    One address making forty signups is caught instantly, so they spread across proxies. Data centre addresses are cheap and easy to spot. Residential proxies route through real people's home connections, so they do not look like infrastructure, because they are not.

  4. 04

    Run the accounts through

    Usually by hand at first, to learn the exact minimum that triggers the payout, then repeated. The behaviour is deliberately boring. Each account does the least work that qualifies and nothing more.

  5. 05

    Collect it all in one place

    This is the step that is hard to hide. The money has to end up somewhere they control, so many accounts converge on a handful of payout destinations.

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. A catch-all domain gives one person infinite valid addresses, and plus tags give them infinite variants of a single real inbox. This stops nobody who is trying.

One account per IP address

It blocks the proxies you can already see, and it blocks real users on shared connections: student housing, offices, mobile carriers, entire countries. It costs you real customers while the farmer buys another proxy for a dollar.

Blocking throwaway email domains from a list

Worth doing, nowhere near enough. Public lists run weeks behind new domains, and a farmer who owns an ordinary-looking domain never appears on one at all.

Requiring phone verification

It raises the cost per account, which genuinely helps, and it does not change the economics. Services sell verification codes for cents. It turns an unlimited attack into a cheap one, and it costs you real signups on the way.

Reviewing it by hand afterwards

It works, it does not scale, and the money has usually gone. 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.

What the email resolves to, not whether it is valid

Kaidn checks this

A farmed address usually passes every validity check, because it is a real working address. What separates it is the domain's age and history, whether it is a throwaway or a catch-all, and whether the address is a plus-tagged variant of an inbox you have already seen.

Speed through the whole funnel, not just at signup

Kaidn checks this

Farms are limited by human effort, so they clump. Bursts of registrations, and more tellingly bursts of the same qualifying action, look nothing like people arriving on their own.

Which network the traffic comes from

Kaidn checks this

A yes/no proxy check misses residential proxies entirely. What still works is that abuse concentrates in particular networks, and you can measure that concentration across your traffic over time.

Device and browser that do not add up

Kaidn checks this

Farmed sessions usually run from a few machines, and the tells are in the mismatches. A browser claiming to be one platform while its graphics stack says another, or signs of a script in a session that should be someone tapping a phone.

Where the money goes

you build this

The strongest single signal in reward fraud, and the most overlooked. Accounts that share nothing at signup often share a destination at withdrawal. You only see that link if you score the payout as well as the registration.

Bonus abuse gets underestimated because no single case looks like fraud.

Nobody stole a 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 ordinary, and any rule strict enough to catch it on its own will also catch a real user having a normal day.

You are not judging one account#

The first mistake nearly everyone makes is trying to decide whether this signup is legitimate, using only what this signup carries.

On its own, a farmed account has a working email, a plausible name, a real device, and an IP address that belongs to somebody's house. There is nothing there to catch.

The abuse is not in the account. It is in the connections between accounts, and you only see those if you are holding the other ones too. That is why a page full of per-account validation rules never holds the line. The teams who beat this started scoring the connections instead of the fields.

Detecting it, concretely#

Two calls. One at registration, one when money moves. The first is cheap insurance. The second is where a farm stops looking like forty separate people.

app/api/signup/route.ts
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 acting on here, and what each is actually telling you:

reasoncheckwhat it means
email_reuseemailReusethis mailbox already sits behind other accounts, once the aliasing is stripped
aliased_addressemailRiskdot or plus tricks pointing at an inbox you have seen
disposable_emailemailRiska known throwaway provider
device_reusedeviceReusethis browser has carried other accounts
abusive_asnipRiskthis network shows up in abuse far more than its share of traffic
device_velocityvelocitythis device is moving through the funnel faster than a person does

Hold the bonus, not the account#

The cheapest block is the one on the payout, not the registration.

Get it wrong at signup and you lose a real customer. Get it wrong on a bonus and you lose a bonus.

app/api/claim-bonus/route.ts
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 you eventually find out what the account really was, send it back. A confirmed farm three weeks later is the only ground truth the engine ever gets:

close the loop
await kaidn.label({ event_id, label: "confirmed_fraud" });

Score the payout, not just the signup#

If you take one thing from this page, take this. Registration on its own is the wrong place to catch reward fraud, because at registration the farmer has told you almost nothing.

By the time money moves, they have told you a lot. Which accounts qualified in the same odd way. In the same window. Through the same networks. And above all, where the money is going.

That last one is the moment forty independent-looking accounts stop looking independent. Scoring both ends costs you one extra call and completely changes what you can see.

Decide what you actually want to happen#

Blocking is not always right, and treating this as yes-or-no is how good users get hurt.

A lot of these cases are genuinely ambiguous. A real user on a shared connection. A returning customer on a new phone. A real signup that arrived in a burst because you ran an ad that morning. Those belong in review, not in a block.

The difference between a fraud tool you can live with and one you cannot is usually whether it lets you set that line yourself. Ours returns allow, review or block, with the checks that fired and what each one counted for, so you can see which signal pushed a user over and move the line if it is wrong.

What to do first, before buying anything#

Take your last thirty days of payouts and group them by where the money went.

If a small number of destinations account for a big share of the accounts, you have bonus abuse and you now know how big it is. That costs nothing, takes an afternoon, and is a better basis for deciding what to spend than any vendor's estimate, ours included.

Frequently asked questions

What is bonus abuse?

One person creates many accounts, has each do the minimum the offer requires, and collects the bonus over and over. No card is stolen and no account is broken into, which is exactly what makes it hard. Every individual account looks completely ordinary. The abuse lives in the connections between accounts, not 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 adding dots or plus tags gives them infinite variants of a single real Gmail inbox. The defence that works is not counting addresses but resolving them. Strip the tricks down to the real 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 genuinely helps, and it does not change the economics. Services sell verification codes for cents, so it turns an unlimited attack into a cheap one, and it costs you real signups on the way. Treat it as friction that buys time, not as a control.

Which signal actually catches a farm?

In practice: mailbox reuse once the aliasing is stripped, and device reuse backed up by the network, weighed together with how fast the accounts arrived. One signal alone rarely convicts. A device seen 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 next to account_count.

Should I score the signup or the payout?

Both. If you can only afford one call, score the payout. At registration the farmer has told you almost nothing. By the time the money moves they have told you which accounts qualified in the same odd way, in the same window, through the same networks. Kaidn scores the cashout event. Matching up where 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.

Other fraud types