Referral Program System Low-Level Design: Tracking, Attribution, and Reward Disbursement

Referral Program System: Low-Level Design

A referral program drives user acquisition by rewarding existing users who bring in new ones. The system must reliably generate unique codes, attribute signups to referrers, trigger rewards at configurable milestones, and defend against fraud. This post walks through each component at the implementation level.

Referral Code Generation

Each user gets one referral code. The simplest approach encodes the user's ID plus a random salt using base62 (characters A-Z, a-z, 0-9), then truncates to 8 characters. Because user IDs are monotonically increasing integers, two codes generated for different users will never collide after the salt is mixed in.


import hashlib, string, random

BASE62 = string.ascii_uppercase + string.ascii_lowercase + string.digits

def _base62_encode(n: int) -> str:
    result = []
    while n:
        result.append(BASE62[n % 62])
        n //= 62
    return ''.join(reversed(result)) or BASE62[0]

def generate_referral_code(user_id: int, salt: str = "") -> str:
    """Deterministic 8-char base62 code for a given user_id + salt."""
    combined = f"{user_id}-{salt or _random_salt()}"
    digest = int(hashlib.sha256(combined.encode()).hexdigest(), 16)
    raw = _base62_encode(digest)
    return raw[:8].upper()

def _random_salt(length: int = 8) -> str:
    return ''.join(random.choices(BASE62, k=length))

Store the salt alongside the code so the same value is reproduced on re-generation. Index the code column with a unique constraint to catch any birthday-paradox collision in the extremely unlikely case it occurs.

Attribution: Cookie to Signup

When a visitor lands on the site via a referral link (?ref=CODE8CHR), the frontend writes the code to a first-party cookie with a 30-day expiry. On signup, the backend reads the cookie value and calls attribute_referral.


from datetime import datetime, timezone

def attribute_referral(code: str, new_user_id: int, db) -> int | None:
    """
    Links new_user_id to the referrer identified by code.
    Returns referral_id on success, None if code invalid or already used.
    """
    row = db.fetchone(
        "SELECT id, referrer_id FROM referral WHERE code = %s AND status = 'pending'",
        (code,)
    )
    if not row:
        return None
    referral_id, referrer_id = row
    if referrer_id == new_user_id:
        return None  # self-referral guard
    db.execute(
        """UPDATE referral
           SET referee_id = %s, status = 'attributed', attributed_at = %s
           WHERE id = %s AND referee_id IS NULL""",
        (new_user_id, datetime.now(timezone.utc), referral_id)
    )
    if db.rowcount == 0:
        return None  # race condition: already attributed
    return referral_id

The WHERE referee_id IS NULL clause prevents a race condition where two concurrent requests try to attribute the same code. Only one UPDATE will match; the other returns rowcount 0.

Multi-Step Milestone Rewards

Rewards need not fire at signup alone. A common pattern releases a smaller reward on signup and a larger one on first purchase. The milestone list is configurable per campaign. The trigger_milestone function is idempotent: calling it twice for the same (referral_id, milestone_type) pair is a no-op.


def trigger_milestone(referral_id: int, milestone_type: str, db, reward_engine) -> bool:
    """
    Records a milestone and enqueues reward disbursement if not already triggered.
    milestone_type: 'signup' | 'first_purchase' | 'first_deposit' etc.
    """
    inserted = db.execute(
        """INSERT INTO referral_milestone (referral_id, milestone_type, triggered_at)
           VALUES (%s, %s, %s)
           ON CONFLICT (referral_id, milestone_type) DO NOTHING""",
        (referral_id, milestone_type, datetime.now(timezone.utc))
    )
    if db.rowcount == 0:
        return False  # already triggered

    campaign = db.fetchone(
        "SELECT * FROM referral WHERE id = %s", (referral_id,)
    )
    reward_engine.enqueue(referral_id, milestone_type, campaign)
    return True

Reward Strategy (Pluggable)

Three reward types are common: account credit, a discount coupon, or cash payout. Each implements a common interface:

  • CreditReward — adds balance to the user's wallet ledger
  • DiscountReward — generates a single-use coupon code
  • CashReward — queues a Stripe or PayPal payout

The reward engine selects the strategy based on the campaign configuration stored in the database. Adding a new reward type means adding a class and a row in the campaign config; no existing code changes.

Fraud Detection

Several checks run before attribution is committed:

  1. Self-referral — blocked in attribute_referral by comparing referrer_id == new_user_id.
  2. Device fingerprint — store a device token in the cookie; if the same token appeared on the referrer's session, reject.
  3. IP velocity — count referral attributions from the same /24 subnet in the last 24 hours; trip a threshold (e.g., 5) to flag for manual review rather than hard-block.
  4. Email domain — bulk sign-ups from the same disposable domain (mailinator.com, etc.) are flagged automatically.

Fraud checks should be non-blocking: store the referral with status = 'pending_review' and process rewards asynchronously after the fraud pipeline clears. This keeps the signup flow fast while still catching abuse.

Database Schema


CREATE TABLE referral (
    id              BIGSERIAL PRIMARY KEY,
    referrer_id     BIGINT NOT NULL REFERENCES users(id),
    referee_id      BIGINT REFERENCES users(id),
    code            CHAR(8) NOT NULL UNIQUE,
    status          VARCHAR(32) NOT NULL DEFAULT 'pending',
    attributed_at   TIMESTAMPTZ,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_referral_code ON referral(code);
CREATE INDEX idx_referral_referrer ON referral(referrer_id);

CREATE TABLE referral_milestone (
    id              BIGSERIAL PRIMARY KEY,
    referral_id     BIGINT NOT NULL REFERENCES referral(id),
    milestone_type  VARCHAR(64) NOT NULL,
    triggered_at    TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    UNIQUE (referral_id, milestone_type)
);

CREATE TABLE referral_reward (
    id              BIGSERIAL PRIMARY KEY,
    referral_id     BIGINT NOT NULL REFERENCES referral(id),
    beneficiary_id  BIGINT NOT NULL REFERENCES users(id),
    reward_type     VARCHAR(32) NOT NULL,   -- 'credit' | 'discount' | 'cash'
    amount          NUMERIC(12,2),
    currency        CHAR(3) DEFAULT 'USD',
    disbursed_at    TIMESTAMPTZ,
    status          VARCHAR(32) NOT NULL DEFAULT 'pending'
);
CREATE INDEX idx_referral_reward_beneficiary ON referral_reward(beneficiary_id);

The UNIQUE (referral_id, milestone_type) constraint on referral_milestone enforces idempotency at the database level, which is the right place for it.

End-to-End Flow Summary

  1. User A visits /dashboard and copies their referral link (?ref=A3B9K2QR).
  2. User B clicks the link. Browser stores ref=A3B9K2QR in a cookie.
  3. User B signs up. Backend calls attribute_referral('A3B9K2QR', user_b_id).
  4. Attribution succeeds. trigger_milestone(referral_id, 'signup') fires; User A gets a $5 credit.
  5. User B completes first purchase. trigger_milestone(referral_id, 'first_purchase') fires; User A gets an additional $15 credit.

{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “How do you ensure referral codes are unique?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Codes are generated by hashing user_id plus a random salt through SHA-256, base62-encoding the digest, and taking the first 8 characters. A UNIQUE database constraint on the code column catches any theoretical collision. The salt is stored so the code is reproducible without regeneration.”
}
},
{
“@type”: “Question”,
“name”: “What happens if a user switches devices before signing up?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “The referral code is stored in a first-party cookie. If the user switches devices, the cookie is absent and attribution does not occur automatically. A common mitigation is to embed the referral code in the signup link itself and store it server-side in the user's registration session, independent of cookies.”
}
},
{
“@type”: “Question”,
“name”: “How are multi-step milestones triggered reliably?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Each business event (signup, first purchase) calls trigger_milestone with the relevant milestone type. The function uses INSERT … ON CONFLICT DO NOTHING plus a unique constraint on (referral_id, milestone_type), making it safe to call multiple times. Reward disbursement is enqueued asynchronously, decoupled from the user-facing transaction.”
}
},
{
“@type”: “Question”,
“name”: “How does fraud prevention avoid blocking legitimate users?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Hard blocks are reserved for clear self-referral (same user ID). Softer signals like shared IP subnet or device fingerprint move the referral to a pending_review state rather than rejecting it outright. Rewards are withheld until the fraud pipeline clears, keeping signup latency low while still protecting the program.”
}
}
]
}

{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “How are referral codes generated to ensure uniqueness?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “A base62 encoding of a hash combining the user ID and a random salt produces an 8-character code; a unique DB constraint rejects collisions and triggers regeneration.”
}
},
{
“@type”: “Question”,
“name”: “How is attribution tracked when a user visits on one device and signs up on another?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “The referral code is stored server-side in a session keyed by a persistent cookie; the code is attached to the account on signup regardless of device.”
}
},
{
“@type”: “Question”,
“name”: “How are milestone rewards triggered idempotently?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “INSERT INTO ReferralMilestone … ON CONFLICT DO NOTHING ensures only one reward is issued per (referral_id, milestone_type) pair even under concurrent requests.”
}
},
{
“@type”: “Question”,
“name”: “How does the system detect self-referral fraud?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “On attribution, the system checks that the referee device fingerprint and IP do not match the referrer's registration data; matching records are flagged and the referral invalidated.”
}
}
]
}

See also: Netflix Interview Guide 2026: Streaming Architecture, Recommendation Systems, and Engineering Excellence

See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering

See also: Shopify Interview Guide

Scroll to Top