← Back to DevBytes

AI Agent Referral Programs: Growing Through Word of Mouth

Understanding AI Agent Referral Programs

AI agent referral programs are structured incentive systems that leverage existing users to drive adoption of AI-powered agents — whether they're chatbots, autonomous assistants, or API-based reasoning tools. Unlike traditional software referral programs, these systems often involve the agent itself acting as the referral engine: detecting usage patterns, identifying ideal moments to suggest sharing, and even generating personalized referral links complete with context-rich previews for potential new users.

At their core, these programs treat word-of-mouth growth as an engineering problem rather than a purely marketing one. When an AI agent delivers real value — solving a complex coding problem, negotiating a better price, or automating a tedious workflow — that moment of delight becomes fertile ground for a referral invitation. The agent can intelligently time the ask, tailor the message to the recipient's context, and track the entire viral loop from impression to conversion.

Key Components of an AI Agent Referral System

A production-ready referral system for AI agents typically involves these interconnected pieces:

Why Referral Programs Matter for AI Agents

AI agents face a unique adoption challenge. They're often invisible in operation — running in background processes, API calls, or chat windows — and their value can be difficult to articulate in traditional ad copy. A static landing page can't demonstrate the fluid intelligence of a well-tuned agent the way a live interaction can. This is where referrals become transformative.

When an existing user shares an agent with a colleague, they rarely just send a link. They share context: "This agent helped me refactor my entire codebase in an afternoon" or "It negotiated my SaaS contracts and saved me 40%." That narrative framing, paired with the recipient's existing trust in the sender, dramatically increases conversion rates compared to cold acquisition channels.

Furthermore, AI agent usage tends to be sticky once integrated into a workflow. A developer who starts using a coding agent for daily tasks is likely to remain an active user for months or years. Referral-acquired users often come pre-educated by their inviter, reducing onboarding friction and support costs. The lifetime value math strongly favors referral growth over paid acquisition for most agent products.

Building the Referral Infrastructure

Let's walk through a practical implementation. We'll build a referral system for an AI coding agent, covering both the backend tracking layer and the agent-side integration that decides when to surface referral prompts.

Database Schema for Referral Tracking

Start with a robust schema that captures the full referral lifecycle. Here's a PostgreSQL migration that establishes the core tables:

-- Migration: create_referral_tables.sql

CREATE TABLE referral_codes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    code VARCHAR(16) UNIQUE NOT NULL,
    is_active BOOLEAN DEFAULT true,
    max_uses INTEGER DEFAULT 0, -- 0 means unlimited
    current_uses INTEGER DEFAULT 0,
    created_at TIMESTAMPTZ DEFAULT now(),
    expires_at TIMESTAMPTZ, -- NULL means never expires
    metadata JSONB DEFAULT '{}'
);

CREATE TABLE referral_links (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    referrer_user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    referral_code_id UUID NOT NULL REFERENCES referral_codes(id),
    share_token VARCHAR(64) UNIQUE NOT NULL,
    channel VARCHAR(32) DEFAULT 'direct', -- direct, email, slack, twitter
    campaign_id VARCHAR(64), -- for grouping referral sources
    context_data JSONB DEFAULT '{}', -- stores agent interaction context
    created_at TIMESTAMPTZ DEFAULT now(),
    total_clicks INTEGER DEFAULT 0,
    total_conversions INTEGER DEFAULT 0
);

CREATE TABLE referral_events (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    referral_link_id UUID REFERENCES referral_links(id),
    event_type VARCHAR(32) NOT NULL, -- click, signup, activation, reward
    ip_address INET,
    user_agent TEXT,
    referee_user_id UUID REFERENCES users(id),
    reward_amount DECIMAL(10,2),
    reward_currency VARCHAR(8) DEFAULT 'credits',
    created_at TIMESTAMPTZ DEFAULT now()
);

CREATE INDEX idx_referral_events_link ON referral_events(referral_link_id);
CREATE INDEX idx_referral_events_referee ON referral_events(referee_user_id);
CREATE INDEX idx_referral_codes_user ON referral_codes(user_id);

This schema separates concerns: referral codes are owned by users and can be reused across multiple links. Each referral link captures the specific sharing context — what channel it was shared through, what agent interaction prompted it — while the events table records the full funnel of clicks, signups, and reward distributions.

Generating Referral Links with Contextual Payloads

When the AI agent decides to surface a referral prompt, it should generate a link that carries rich context. Here's a Python function that creates a referral link with embedded interaction data:

import secrets
import hashlib
from datetime import datetime, timedelta
from typing import Optional, Dict, Any
import json

def generate_referral_link(
    referrer_user_id: str,
    channel: str = "agent_prompt",
    context_data: Optional[Dict[str, Any]] = None,
    expires_in_days: int = 30
) -> dict:
    """
    Creates a referral link with embedded context about the agent interaction
    that triggered the referral prompt. Returns the link object for database
    insertion and a shareable URL.
    """
    # Generate a cryptographically random share token
    share_token = secrets.token_urlsafe(48)
    
    # Hash it for database storage (prevents plaintext token exposure)
    token_hash = hashlib.sha256(share_token.encode()).hexdigest()
    
    # Build context payload if provided
    if context_data is None:
        context_data = {}
    
    # Standardize the context structure
    context_payload = {
        "agent_id": context_data.get("agent_id", "default"),
        "interaction_type": context_data.get("interaction_type", "general"),
        "summary": context_data.get("summary", ""),
        "result_preview": context_data.get("result_preview", ""),
        "prompt_reason": context_data.get("prompt_reason", "value_demonstrated"),
        "timestamp_iso": datetime.utcnow().isoformat(),
        "thread_id": context_data.get("thread_id"),
        "model_version": context_data.get("model_version", "unknown")
    }
    
    expires_at = datetime.utcnow() + timedelta(days=expires_in_days)
    
    referral_link_record = {
        "referrer_user_id": referrer_user_id,
        "share_token_hash": token_hash,
        "share_token_plain": share_token,  # Return to caller for URL construction
        "channel": channel,
        "context_data": context_payload,
        "created_at": datetime.utcnow(),
        "expires_at": expires_at,
        "total_clicks": 0,
        "total_conversions": 0
    }
    
    # Construct the full referral URL
    base_url = "https://agent.example.com/refer"
    referral_url = f"{base_url}/{share_token}"
    
    return {
        "record": referral_link_record,
        "url": referral_url,
        "token": share_token
    }

Attribution Middleware for Web Applications

When a referred visitor arrives, you need to persist attribution data across their session — even if they browse around before signing up. Here's a FastAPI middleware that handles referral tracking on the server side:

from fastapi import FastAPI, Request, Response, HTTPException
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
import hashlib
import json
from datetime import datetime

class ReferralAttributionMiddleware(BaseHTTPMiddleware):
    """
    Middleware that detects referral tokens in incoming requests,
    validates them against the database, and persists attribution
    data in a signed cookie for later conversion tracking.
    """
    
    def __init__(self, app, db_session_factory, cookie_name="ref_attr"):
        super().__init__(app)
        self.db_session_factory = db_session_factory
        self.cookie_name = cookie_name
    
    async def dispatch(self, request: Request, call_next):
        # Extract referral token from URL path or query params
        token = None
        path_parts = request.url.path.strip("/").split("/")
        
        if len(path_parts) >= 2 and path_parts[0] == "refer":
            token = path_parts[1]
        
        if token:
            token_hash = hashlib.sha256(token.encode()).hexdigest()
            
            async with self.db_session_factory() as db:
                result = await db.execute(
                    """
                    SELECT rl.id, rl.referrer_user_id, rl.context_data, rl.channel
                    FROM referral_links rl
                    WHERE rl.share_token_hash = :hash
                    AND rl.expires_at > :now
                    """,
                    {"hash": token_hash, "now": datetime.utcnow()}
                )
                link = result.fetchone()
                
                if link:
                    # Record the click event
                    await db.execute(
                        """
                        INSERT INTO referral_events 
                        (referral_link_id, event_type, ip_address, user_agent)
                        VALUES (:link_id, 'click', :ip, :ua)
                        """,
                        {
                            "link_id": link.id,
                            "ip": request.client.host if request.client else None,
                            "ua": request.headers.get("user-agent", "")
                        }
                    )
                    
                    # Update click counter
                    await db.execute(
                        "UPDATE referral_links SET total_clicks = total_clicks + 1 WHERE id = :id",
                        {"id": link.id}
                    )
                    await db.commit()
                    
                    # Build attribution payload for the cookie
                    attribution_data = {
                        "referral_link_id": str(link.id),
                        "referrer_user_id": str(link.referrer_user_id),
                        "channel": link.channel,
                        "landed_at": datetime.utcnow().isoformat(),
                        "token_hash": token_hash
                    }
                    
                    # Create a response and set the attribution cookie
                    response = await call_next(request)
                    response.set_cookie(
                        self.cookie_name,
                        json.dumps(attribution_data),
                        max_age=86400 * 30,  # 30 days
                        httponly=True,
                        secure=True,
                        samesite="lax"
                    )
                    return response
        
        # No referral token — check if attribution cookie already exists
        existing_attr = request.cookies.get(self.cookie_name)
        response = await call_next(request)
        
        if existing_attr:
            # Refresh the cookie's max_age to extend attribution window
            response.set_cookie(
                self.cookie_name,
                existing_attr,
                max_age=86400 * 30,
                httponly=True,
                secure=True,
                samesite="lax"
            )
        
        return response

Conversion Tracking on Signup

The final piece of the attribution puzzle fires when a referred visitor converts. This function reads the attribution cookie, validates it hasn't been tampered with, and records the conversion event along with reward distribution:

async def process_referral_conversion(
    db_session,
    referee_user_id: str,
    attribution_cookie: str,
    reward_amount: float = 10.0,
    reward_currency: str = "credits"
) -> dict:
    """
    Called upon successful user signup. Reads the attribution cookie,
    validates the referral link, marks the conversion, and distributes
    rewards to both referrer and referee.
    
    Returns a summary of actions taken.
    """
    try:
        attribution_data = json.loads(attribution_cookie)
    except (json.JSONDecodeError, TypeError):
        return {"status": "no_attribution", "message": "Invalid attribution cookie"}
    
    required_fields = ["referral_link_id", "referrer_user_id", "token_hash"]
    if not all(field in attribution_data for field in required_fields):
        return {"status": "no_attribution", "message": "Missing attribution fields"}
    
    referral_link_id = attribution_data["referral_link_id"]
    referrer_user_id = attribution_data["referrer_user_id"]
    
    async with db_session() as db:
        # Verify the referral link is still valid
        result = await db.execute(
            """
            SELECT id, referrer_user_id, referral_code_id, total_conversions
            FROM referral_links
            WHERE id = :link_id 
            AND referrer_user_id = :referrer_id
            AND expires_at > :now
            """,
            {
                "link_id": referral_link_id,
                "referrer_id": referrer_user_id,
                "now": datetime.utcnow()
            }
        )
        link = result.fetchone()
        
        if not link:
            return {"status": "expired_or_invalid", "message": "Referral link is no longer valid"}
        
        # Check for duplicate conversion (same referee can't convert twice on same link)
        duplicate_check = await db.execute(
            """
            SELECT id FROM referral_events
            WHERE referral_link_id = :link_id
            AND referee_user_id = :referee_id
            AND event_type = 'signup'
            """,
            {"link_id": referral_link_id, "referee_id": referee_user_id}
        )
        if duplicate_check.fetchone():
            return {"status": "duplicate", "message": "Conversion already recorded for this user"}
        
        # Record the conversion event
        await db.execute(
            """
            INSERT INTO referral_events
            (referral_link_id, event_type, referee_user_id, reward_amount, reward_currency)
            VALUES (:link_id, 'signup', :referee_id, :amount, :currency)
            """,
            {
                "link_id": referral_link_id,
                "referee_id": referee_user_id,
                "amount": reward_amount,
                "currency": reward_currency
            }
        )
        
        # Update conversion counter
        await db.execute(
            "UPDATE referral_links SET total_conversions = total_conversions + 1 WHERE id = :id",
            {"id": referral_link_id}
        )
        
        # Distribute rewards (idempotent operation — use a transaction)
        # Reward the referrer
        await db.execute(
            """
            UPDATE users SET credit_balance = credit_balance + :amount
            WHERE id = :user_id
            """,
            {"amount": reward_amount, "user_id": referrer_user_id}
        )
        
        # Reward the referee (new user)
        await db.execute(
            """
            UPDATE users SET credit_balance = credit_balance + :amount
            WHERE id = :user_id
            """,
            {"amount": reward_amount * 0.5, "user_id": referee_user_id}
        )
        
        # Record reward events
        await db.execute(
            """
            INSERT INTO referral_events
            (referral_link_id, event_type, referee_user_id, reward_amount, reward_currency)
            VALUES 
            (:link_id, 'reward_referrer', :referee_id, :ref_amount, :currency)
            """,
            {
                "link_id": referral_link_id,
                "referee_id": referee_user_id,
                "ref_amount": reward_amount,
                "currency": reward_currency
            }
        )
        
        await db.commit()
        
        return {
            "status": "converted",
            "referral_link_id": str(referral_link_id),
            "referrer_rewarded": reward_amount,
            "referee_rewarded": reward_amount * 0.5,
            "currency": reward_currency
        }

Integrating the Referral Prompt into the AI Agent

The most distinctive aspect of an AI agent referral program is how the agent itself decides to surface the referral invitation. This requires careful prompt engineering and context evaluation to avoid annoying users with poorly timed requests.

Agent-Side Referral Decision Logic

Here's a Python implementation that evaluates conversation context and determines whether to inject a referral suggestion into the agent's response stream:

from dataclasses import dataclass, field
from enum import Enum
from typing import List, Optional
import time

class ReferralSentiment(Enum):
    POSITIVE = "positive"
    NEUTRAL = "neutral"
    NEGATIVE = "negative"

@dataclass
class InteractionContext:
    user_id: str
    thread_id: str
    message_count: int
    user_satisfaction_signals: List[str] = field(default_factory=list)
    explicit_thanks: bool = False
    problem_resolved: bool = False
    time_spent_minutes: float = 0.0
    agent_performance_score: float = 0.0  # 0-1 scale from internal evaluation

class ReferralPromptDecider:
    """
    Decides whether an AI agent should surface a referral prompt
    based on conversation quality, user sentiment, and timing constraints.
    """
    
    def __init__(
        self,
        min_messages_before_prompt: int = 6,
        min_satisfaction_score: float = 0.75,
        cooldown_minutes: int = 120,
        max_prompts_per_user_per_day: int = 2,
        exclude_negative_sentiment: bool = True
    ):
        self.min_messages = min_messages_before_prompt
        self.min_satisfaction = min_satisfaction_score
        self.cooldown_minutes = cooldown_minutes
        self.max_prompts_per_day = max_prompts_per_user_per_day
        self.exclude_negative = exclude_negative_sentiment
        self._prompt_history = {}  # user_id -> list of timestamps
    
    def should_prompt(self, context: InteractionContext) -> tuple[bool, Optional[str]]:
        """
        Evaluates whether to show a referral prompt and returns
        a decision along with an optional reason string.
        """
        user_id = context.user_id
        
        # Check message volume — need meaningful interaction
        if context.message_count < self.min_messages:
            return False, "insufficient_interaction_depth"
        
        # Check satisfaction threshold
        if context.agent_performance_score < self.min_satisfaction:
            return False, "below_satisfaction_threshold"
        
        # Don't prompt during negative experiences
        if self.exclude_negative:
            negative_signals = ["frustrated", "angry", "confused", "error", "bug"]
            for signal in context.user_satisfaction_signals:
                if signal.lower() in negative_signals:
                    return False, "negative_sentiment_detected"
        
        # Enforce cooldown between prompts
        now = time.time()
        user_prompts = self._prompt_history.get(user_id, [])
        
        # Clean old entries outside cooldown window
        user_prompts = [
            ts for ts in user_prompts
            if (now - ts) < (self.cooldown_minutes * 60)
        ]
        
        if user_prompts:
            last_prompt_ago = now - user_prompts[-1]
            if last_prompt_ago < (self.cooldown_minutes * 60):
                return False, f"cooldown_active_{int(last_prompt_ago)}s_ago"
        
        # Check daily cap
        daily_prompts = [
            ts for ts in self._prompt_history.get(user_id, [])
            if (now - ts) < 86400  # 24 hours
        ]
        if len(daily_prompts) >= self.max_prompts_per_day:
            return False, "daily_prompt_cap_reached"
        
        # All checks passed — recommend prompting
        return True, "conditions_met"
    
    def record_prompt_shown(self, user_id: str):
        """Record that a prompt was displayed to enforce cooldown and caps."""
        now = time.time()
        if user_id not in self._prompt_history:
            self._prompt_history[user_id] = []
        self._prompt_history[user_id].append(now)
        
        # Prune entries older than 24 hours to prevent memory bloat
        self._prompt_history[user_id] = [
            ts for ts in self._prompt_history[user_id]
            if (now - ts) < 86400
        ]

Prompt Template Engineering for the Referral Message

The referral message itself should feel native to the agent's voice and contextually relevant. Here's how to construct a dynamic prompt template that the agent can inject into its response:

def build_referral_prompt_template(
    context: InteractionContext,
    referral_url: str,
    agent_name: str = "CodeCompanion"
) -> str:
    """
    Constructs a context-aware referral prompt that feels natural
    within the agent's conversational flow. Returns a string that
    can be appended to the agent's response.
    """
    
    # Base templates for different interaction types
    templates = {
        "problem_solved": (
            f"\n\nI'm glad I could help with this! 🎉\n"
            f"If you found this useful, you can share {agent_name} with a teammate "
            f"using your referral link. They'll get extra credits to start, and "
            f"so will you: {referral_url}\n"
            f"(I only mention this when I'm confident I've actually been helpful — "
            f"feel free to ignore if now's not the right time!)"
        ),
        "workflow_automation": (
            f"\n\nBy the way — since this automation saved you some serious time, "
            f"you might know someone else on your team who'd benefit from {agent_name}.\n"
            f"Here's your referral link if you'd like to share: {referral_url}\n"
            f"Both you and they will receive bonus credits when they sign up."
        ),
        "learning_assistance": (
            f"\n\nI hope this explanation was clear! If you have colleagues learning "
            f"similar concepts, {agent_name} can help them too.\n"
            f"Your referral link (with bonus credits for both of you): {referral_url}"
        ),
        "general_value": (
            f"\n\nThanks for the great conversation! If you know someone who could "
            f"benefit from {agent_name}, here's your referral link: {referral_url}\n"
            f"You'll both receive credits when they join."
        )
    }
    
    # Select template based on interaction type
    interaction_type = context.agent_performance_score > 0.9 and "problem_solved" \
        or "general_value"
    
    if context.explicit_thanks:
        interaction_type = "problem_solved"
    
    template = templates.get(interaction_type, templates["general_value"])
    
    return template

Full Agent Response Pipeline with Referral Injection

Here's the complete integration that ties together the decision logic, prompt construction, and link generation into a single agent response pipeline:

class AgentReferralPipeline:
    """
    Orchestrates the full referral flow: evaluates context, generates
    referral links when appropriate, and injects prompts into agent responses.
    """
    
    def __init__(self, db_session_factory, agent_name="CodeCompanion"):
        self.db_factory = db_session_factory
        self.decider = ReferralPromptDecider()
        self.agent_name = agent_name
    
    async def process_agent_response(
        self,
        user_id: str,
        thread_id: str,
        agent_response_text: str,
        interaction_context: InteractionContext
    ) -> str:
        """
        Takes the agent's intended response and optionally appends
        a referral prompt if conditions are met. Returns the final
        response text to be shown to the user.
        """
        
        # Evaluate whether to prompt
        should_prompt, reason = self.decider.should_prompt(interaction_context)
        
        if not should_prompt:
            # Log the decision for analytics (omitted for brevity)
            return agent_response_text
        
        # Generate a referral link with context
        link_data = generate_referral_link(
            referrer_user_id=user_id,
            channel="agent_prompt",
            context_data={
                "agent_id": self.agent_name,
                "interaction_type": "problem_solved" if interaction_context.explicit_thanks else "general",
                "summary": agent_response_text[:200],  # Truncated for payload size
                "prompt_reason": reason,
                "thread_id": thread_id,
                "model_version": "v2.4.1"
            }
        )
        
        # Store the referral link in the database
        async with self.db_factory() as db:
            await db.execute(
                """
                INSERT INTO referral_links 
                (referrer_user_id, referral_code_id, share_token, channel, context_data)
                VALUES (:user_id, 
                    (SELECT id FROM referral_codes WHERE user_id = :user_id AND is_active = true LIMIT 1),
                    :token_hash, :channel, :context)
                """,
                {
                    "user_id": user_id,
                    "token_hash": link_data["record"]["share_token_hash"],
                    "channel": "agent_prompt",
                    "context": json.dumps(link_data["record"]["context_data"])
                }
            )
            await db.commit()
        
        # Build the referral prompt
        prompt_text = build_referral_prompt_template(
            interaction_context,
            link_data["url"],
            self.agent_name
        )
        
        # Record that we showed a prompt
        self.decider.record_prompt_shown(user_id)
        
        # Return the agent response with the prompt appended
        final_response = agent_response_text + "\n" + prompt_text
        
        return final_response

Fraud Prevention and Program Integrity

Referral programs attract abuse. Self-referrals (creating fake accounts to claim rewards), bot-driven signups, and reward farming rings can drain your budget and pollute your user base. Building defenses into the system from day one is essential.

Multi-Layer Fraud Detection

Implement a fraud scoring system that evaluates each conversion attempt across multiple signals:

from dataclasses import dataclass
from typing import List, Optional
import ipaddress
import re

@dataclass
class FraudAssessment:
    score: float  # 0.0 (clean) to 1.0 (certain fraud)
    flags: List[str]
    recommended_action: str  # "allow", "review", "block"

class ReferralFraudDetector:
    """
    Multi-signal fraud detection for referral conversions.
    Combines IP analysis, velocity checks, device fingerprinting,
    and behavioral patterns to assign fraud scores.
    """
    
    def __init__(
        self,
        high_risk_ip_ranges: List[str] = None,
        block_threshold: float = 0.8,
        review_threshold: float = 0.4
    ):
        self.high_risk_ranges = high_risk_ip_ranges or [
            "127.0.0.0/8",  # localhost abuse
        ]
        self.block_threshold = block_threshold
        self.review_threshold = review_threshold
        # Convert CIDR ranges to ipaddress objects
        self._parsed_ranges = [
            ipaddress.ip_network(r) for r in self.high_risk_ranges
        ]
    
    async def assess_conversion(
        self,
        db_session,
        referrer_user_id: str,
        referee_ip: str,
        referee_user_agent: str,
        referee_email: str,
        referral_link_id: str
    ) -> FraudAssessment:
        """
        Evaluates a conversion attempt and returns a fraud assessment.
        """
        flags = []
        score = 0.0
        
        # Check 1: IP address analysis
        try:
            ip_addr = ipaddress.ip_address(referee_ip)
            for network in self._parsed_ranges:
                if ip_addr in network:
                    flags.append("high_risk_ip_range")
                    score += 0.3
        except ValueError:
            flags.append("invalid_ip")
            score += 0.2
        
        # Check 2: Check if referrer and referee share IP address
        async with db_session() as db:
            result = await db.execute(
                """
                SELECT ip_address FROM referral_events
                WHERE referral_link_id = :link_id AND event_type = 'click'
                ORDER BY created_at DESC LIMIT 1
                """,
                {"link_id": referral_link_id}
            )
            referrer_click = result.fetchone()
            
            if referrer_click and referrer_click.ip_address:
                if str(ip_addr) == str(referrer_click.ip_address):
                    flags.append("same_ip_as_referrer")
                    score += 0.4  # Strong signal of self-referral
        
        # Check 3: Velocity — how many conversions from this referrer recently?
        result = await db.execute(
            """
            SELECT COUNT(*) as conv_count
            FROM referral_events re
            JOIN referral_links rl ON re.referral_link_id = rl.id
            WHERE rl.referrer_user_id = :referrer_id
            AND re.event_type = 'signup'
            AND re.created_at > NOW() - INTERVAL '24 hours'
            """,
            {"referrer_id": referrer_user_id}
        )
        recent_conversions = result.fetchone().conv_count
        
        if recent_conversions > 5:
            flags.append(f"high_velocity_{recent_conversions}_24h")
            score += min(0.5, recent_conversions * 0.1)
        
        # Check 4: Email domain analysis
        email_domain = referee_email.split("@")[-1].lower()
        disposable_domains = {
            "mailinator.com", "guerrillamail.com", "tempmail.com",
            "10minutemail.com", "yopmail.com", "sharklasers.com",
            "spamgourmet.com", "trashmail.com"
        }
        if email_domain in disposable_domains:
            flags.append("disposable_email")
            score += 0.3
        
        # Check 5: User agent consistency
        known_bot_agents = ["headless", "selenium", "puppeteer", "playwright"]
        ua_lower = referee_user_agent.lower()
        for bot_signal in known_bot_agents:
            if bot_signal in ua_lower:
                flags.append("bot_user_agent")
                score += 0.3
                break
        
        # Determine action based on score
        if score >= self.block_threshold:
            action = "block"
        elif score >= self.review_threshold:
            action = "review"
        else:
            action = "allow"
        
        return FraudAssessment(
            score=score,
            flags=flags,
            recommended_action=action
        )

Best Practices for AI Agent Referral Programs

Building the technical infrastructure is only half the equation. These practices separate successful referral programs from those that annoy users or collapse under abuse.

Timing Is Everything

The most common failure mode is prompting for referrals at the wrong moment. An agent that asks for a referral after failing to solve a problem destroys trust. Conversely, an agent that never asks leaves growth on the table. Implement explicit satisfaction signals — the user saying "thanks, that was perfect" or the agent successfully completing a multi-step task — as gating conditions. The ReferralPromptDecider class above enforces this, but you should continuously tune thresholds based on actual conversion data.

Respect the User's Attention

Cooldowns and daily caps aren't just technical safeguards — they're UX requirements. A user who sees three referral prompts in a single session will mentally mute the agent entirely. The cooldown mechanism should be conservative: 2-4 hours between prompts is a reasonable starting point. Additionally, provide a persistent, low-friction way for users to access their referral link on demand (a dashboard widget, a slash command like /referral) so they aren't dependent on the agent's timing.

Make Rewards Meaningful and Immediate

Credit-based rewards work well for developer-focused agents. The reward should be substantial enough to motivate sharing — 10-20% of a typical monthly usage allowance is a good benchmark. Crucially, rewards must be granted instantly upon conversion, not batched weekly. The psychological reinforcement of seeing credits appear immediately after a colleague signs up creates a positive feedback loop that encourages further sharing.

Transparency Builds Trust

Never hide the fact that a message is a referral prompt. The templates in the build_referral_prompt_template function explicitly acknowledge the promotional nature: "I only mention this when I'm confident I've actually been helpful." This honesty respects the user's intelligence and actually increases conversion rates compared to manipulative or sneaky approaches. Provide a clear opt-out mechanism — a simple "Don't ask me about referrals" preference that the agent honors globally.

Monitor and Iterate on Metrics

Track the full funnel: impressions → clicks → signups → activations → referrer retention. A referral program that drives signups but those users never activate is worse than no program at all — it pollutes your user base. Build dashboards that surface the conversion rate at each stage, broken down by agent interaction type, channel, and user segment. Use this data to refine both the prompt timing logic and the reward structure.

Implement Graduated Reward Tiers

Simple flat rewards work initially, but sophisticated programs introduce tiers that reward power users who bring in high-quality referrals. Consider implementing a system where the reward increases when the referred user reaches certain milestones — for example, completing 10 agent interactions, or remaining active for 30 days. This aligns incentives: referrers are motivated to bring in users who will actually use the product, not just sign up and abandon it.

# Example graduated reward configuration
GRADUATED_REWARDS = {
    "signup": 10.0,           # Immediate reward on signup
    "first_interaction": 5.0, # Bonus when referee completes first agent task
    "power_user": 25.0,       # Bonus when referee reaches 50 interactions
    "monthly_active": 15.0,   # Bonus when referee is active for 30+ days
}

async def evaluate_graduated_reward(
    db_session,
    referee_user_id: str,
    milestone: str,
    referral_link_id: str
) -> Optional[float]:
    """
    Checks if a graduated reward milestone has been reached
    and hasn't already been awarded for this referral link.
    """
    reward_amount = GRADUATED_REWARDS.get(milestone)
    if not reward_amount:
        return None
    
    async with db_session() as db:
        # Check for duplicate reward
        result = await db.execute(
            """
            SELECT id FROM referral_events
            WHERE referral_link_id = :link_id
            AND referee_user_id = :referee_id
            AND event_type = :milestone
            """,
            {"link_id": referral_link_id, "referee_id": referee_user_id, "milestone": milestone}
        )
        if result.fetch

— Ad —

Google AdSense will appear here after approval

← Back to all articles