A referral program is one of the highest-leverage features you can add to a product, and one of the easiest to get subtly wrong in ways that cost real money. Reward the wrong action and you pay for signups that never convert. Skip fraud checks and you fund a spreadsheet of fake accounts. Launch to everyone at once and you can’t tell whether it worked.

Here’s how to build one that’s structurally sound, and how to prove it’s worth keeping before you bet the growth budget on it.

Decide what you’re rewarding, precisely

“Refer a friend” hides a critical question: rewarded when? The options, in increasing order of how much you should trust them:

  • On signup. Easy to game, weakly correlated with value. Avoid as the sole trigger.
  • On activation. The referred user did the thing that matters (created a project, sent a message, connected an integration). Much better signal.
  • On paid conversion. The referred user became a paying customer. The gold standard for a paid product, and the only trigger that guarantees the referral was worth paying for.

Most healthy programs reward on activation or conversion, not signup. Write this down before you build anything, because it determines your entire data model.

The data model

At minimum you need:

referral_codes   (id, user_id, code, created_at)
referrals        (id, referrer_id, referred_user_id, code,
                  status, created_at, qualified_at, rewarded_at)

status walks through a small state machine: pending (referred user signed up) → qualified (they hit the reward trigger) → rewarded (you’ve credited both sides), plus rejected if it fails a fraud check. Modeling status explicitly, rather than inferring it, is what lets you build an honest dashboard and debug disputes later.

A prompt to scaffold this cleanly:

Design a referral system schema and the state machine for a referral’s lifecycle (pending → qualified → rewarded, plus rejected). Reward trigger is [paid conversion]. Include the API for generating a user’s referral code and for attributing a new signup to a code. Show me the schema and state transitions before any implementation.

Attribution without the leaks

Attribution is where referral programs quietly break. Watch for:

  • Self-referral. Block a user from qualifying their own referral, same email, same payment method, same device fingerprint.
  • Attribution window. A referred click should map to a signup within a sensible window (say 30 days). Store the code at click time in a cookie, then bind it at signup.
  • Last-touch vs first-touch. Pick one and be consistent. First-touch is usually fairer to the referrer.
  • Idempotency. The reward trigger must fire exactly once per referral. Guard it, or a retried webhook pays out twice.

Reward mechanics that don’t get exploited

Two-sided rewards (both referrer and referred get something) consistently outperform one-sided ones. But cap them: a per-user monthly ceiling on total referral rewards turns “unlimited free months by spamming a link” into a bounded, predictable cost. Credit rewards to accounts rather than sending cash where you can, it’s cheaper and keeps value inside your product.

Prove it works before you scale it

This is the part that separates a referral program from a referral experiment. You’ve built it. Should you turn it on for every user and announce it? Not yet, because you can’t yet answer the only question that matters: does it actually drive net-new, activated users at an acceptable cost?

Roll it out as a measured experiment instead:

  1. Enable it for a slice of users rather than everyone, and keep a comparable slice without it as a control. Gate the entire feature, the referral UI, the reward logic, the emails, so you can flip it per-cohort.
  2. Instrument the funnel: codes generated, links shared, referred signups, referred activations, referred conversions, and reward cost. A referral program with lots of shares and no conversions is a leak, not a channel.
  3. Compare cohorts. If the group with referrals doesn’t show more activated users than the control, you’ve learned that cheaply, before you spent a quarter promoting it.
  4. Ramp what works. Once the numbers hold up, widen the rollout. If the reward mechanic turns out to be exploitable, you throttle it back to zero instantly instead of scrambling to disable a shipped, promoted feature.

Keeping the whole program behind a switch has a second benefit: the day someone finds an exploit, and with referral programs, someone eventually will, your response is “flip it off, patch, flip it on,” not an emergency deploy while the abuse continues.

A referral program is a growth bet. Build it soundly, but treat the launch as a test you can pause, not a monument you’ve unveiled.


ShipSilently lets you run a feature as a controlled experiment, on for one cohort, off for a control, and kill it in one click if it misbehaves. Get started free.