# Low Level Design: Referral Service

Source: https://www.techinterview.org/post/3233471608/lld-referral-service/
Updated: 2026-06-30 · techinterview.org

## What Is a Referral Service?

A Referral Service incentivizes existing users to invite new users by rewarding both parties when a successful conversion occurs. It must accurately track who referred whom, prevent self-referrals and fraud, detect when a referral has converted (signed up, made a purchase, etc.), and trigger reward payouts reliably without double-counting.

## Data Model


```
TABLE referral_codes (
  id         BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_id    BIGINT NOT NULL UNIQUE,   -- one code per user
  code       VARCHAR(32) UNIQUE NOT NULL,
  created_at DATETIME DEFAULT NOW()
);

TABLE referrals (
  id              BIGINT PRIMARY KEY AUTO_INCREMENT,
  referrer_id     BIGINT NOT NULL,   -- user who shared the code
  referee_id      BIGINT NOT NULL UNIQUE, -- new user; one referrer only
  code            VARCHAR(32) NOT NULL,
  status          ENUM('pending', 'converted', 'rewarded', 'voided') DEFAULT 'pending',
  conversion_event VARCHAR(64),      -- e.g. 'first_purchase'
  converted_at    DATETIME,
  rewarded_at     DATETIME,
  created_at      DATETIME DEFAULT NOW(),
  INDEX idx_referrer (referrer_id),
  INDEX idx_code (code)
);

TABLE referral_rewards (
  id           BIGINT PRIMARY KEY AUTO_INCREMENT,
  referral_id  BIGINT NOT NULL REFERENCES referrals(id),
  user_id      BIGINT NOT NULL,
  reward_type  ENUM('credit', 'coupon', 'points') NOT NULL,
  amount       DECIMAL(10,2),
  issued_at    DATETIME DEFAULT NOW()
);
```


## Core Workflow

  - **Code generation:** On user signup, generate a unique alphanumeric code (e.g., 8 characters, base-36) and insert into `referral_codes`.

  - **Attribution:** When a new visitor lands via a referral link, store the code in a cookie or session. On signup, look up the code, validate the referrer exists and is not the same account, then insert a `referrals` row with `status = pending`.

  - **Conversion detection:** The service subscribes to domain events (e.g., `order.completed`) via a message queue. On receiving a qualifying event for a referee, it updates `referrals.status` to `converted` and records `converted_at`.

  - **Reward issuance:** A worker transitions status to `rewarded` and inserts rows into `referral_rewards` for both referrer and referee, then calls the appropriate reward subsystem (credit, coupon, or points service).

## Failure Handling

  - **Duplicate conversion events:** The message consumer must be idempotent. Use the `referee_id UNIQUE` constraint and a conditional update: `UPDATE referrals SET status='converted' WHERE referee_id=? AND status='pending'`. Zero rows affected means already processed.

  - **Reward delivery failure:** Issue rewards inside a database transaction alongside the status update. If the downstream call fails, roll back and rely on the queue to redeliver. Use an outbox pattern to guarantee at-least-once reward delivery.

  - **Fraud / self-referral:** At attribution time, reject if `referrer_id = new_user_id`, if the referrer account is flagged, or if the device fingerprint matches the referrer.

  - **Code expiry:** Support optional `expires_at` on `referral_codes` for campaign-limited programs.

## Scalability Considerations

  - **Event-driven architecture:** Decoupling conversion detection from the order flow via a message queue (Kafka, SQS) prevents the referral logic from adding latency to checkout.

  - **Code lookup caching:** Cache the `referral_codes` table in Redis. A read-through cache with a long TTL works well since codes rarely change.

  - **Analytics separation:** Aggregate referral metrics (conversion rates, top referrers) in a read replica or data warehouse rather than querying the operational database.

  - **Horizontal scaling:** The reward worker pool can scale independently based on queue depth.

## Summary

A Referral Service is fundamentally an event-driven state machine: codes are minted, referrals are attributed, conversion events trigger status transitions, and rewards are issued exactly once. The UNIQUE constraint on `referee_id` is the single most important correctness guarantee in the schema.

See also: [Uber Interview Guide 2026: Dispatch Systems, Geospatial Algorithms, and Marketplace Engineering](https://www.techinterview.org/post/3233460273/uber-interview-guide-2026-dispatch-systems-geospatial-algorithms-and-marketplace-engineering/)

See also: [Airbnb Interview Guide 2026: Search Systems, Trust and Safety, and Full-Stack Engineering](https://www.techinterview.org/post/3233460279/airbnb-interview-guide-2026-search-systems-trust-and-safety-and-full-stack-engineering/)

See also: [Shopify Interview Guide](https://www.techinterview.org/post/3233459924/shopify-interview/)
