Create a powerful referral system that tracks installs, attributes users to referrers, and provides seamless onboarding experiences. Learn how to build a complete referral program with deep linking and deferred deep linking.
Traditional referral programs often lose track of users between click and install. With deep linking and deferred deep linking, you can track the complete user journey from referral click to app install to first action, ensuring accurate attribution and rewards.
Existing user shares personalized referral link via social media, messaging, etc.
Prospect clicks referral link, gets redirected to app store or web page
New user installs app, deferred deep linking system tracks the install
System attributes install to referrer and triggers reward distribution
Create personalized referral links for users:
app.post('/api/generate-referral-link', async (req, res) => { try { const { userId } = req.body; // 1. Get user profile const user = await getUserById(userId); if (!user) { return res.status(404).json({ error: 'User not found' }); } // 2. Create referral link with metadata const referralLink = await createReferralLink(userId, user.username); // 3. Store referral link in database await storeReferralLink(userId, referralLink); res.json({ success: true, referralLink: referralLink, shareText: `Join me on ${appName}! Use my referral link: ${referralLink}` }); } catch (error) { console.error('Referral link generation error:', error); res.status(500).json({ error: 'Failed to generate referral link' }); } }); async function createReferralLink(userId, username) { try { const response = await redirectly.post('/links', { slug: `ref-${username}-${Date.now()}`, target: `https://yourapp.com/onboarding?ref=${userId}`, metadata: { type: 'referral', referrerId: userId, referrerUsername: username, campaign: 'user_referral' } }); return response.data.url; } catch (error) { console.error('Failed to create referral link:', error); throw new Error('Failed to create referral link'); } }
Track app installs and attribute them to referrers:
// Webhook endpoint for app install events app.post('/api/webhook/app-install', async (req, res) => { try { const { deviceId, installData } = req.body; // 1. Check if this install is attributed to a referral const attribution = await checkInstallAttribution(deviceId); if (attribution && attribution.referrerId) { // 2. Record successful referral await recordSuccessfulReferral(attribution); // 3. Trigger reward distribution await distributeReferralReward(attribution.referrerId, attribution.referredUserId); // 4. Send notifications await notifyReferralSuccess(attribution); } res.json({ success: true }); } catch (error) { console.error('App install attribution error:', error); res.status(500).json({ error: 'Attribution failed' }); } }); async function checkInstallAttribution(deviceId) { // Check if device was previously tracked from a referral link const attribution = await db.query( 'SELECT * FROM referral_attributions WHERE device_id = ? AND status = "pending"', [deviceId] ); return attribution.length > 0 ? attribution[0] : null; } async function recordSuccessfulReferral(attribution) { await db.query( 'UPDATE referral_attributions SET status = "completed", completed_at = NOW() WHERE id = ?', [attribution.id] ); // Update user referral stats await db.query( 'UPDATE users SET referral_count = referral_count + 1 WHERE id = ?', [attribution.referrer_id] ); }
Create a service to handle referral functionality:
class ReferralService { static final FlutterRedirectly _redirectly = FlutterRedirectly(); static Future<void> initialize() async { await _redirectly.initialize(RedirectlyConfig( apiKey: 'YOUR_API_KEY', baseUrl: 'https://redirectly.app', enableDebugLogging: true, )); // Listen for app install events _redirectly.onAppInstalled.listen(_handleAppInstall); // Listen for incoming referral links _redirectly.onLinkClick.listen(_handleReferralLink); } static void _handleAppInstall(AppInstallEvent event) { if (event.matched && event.username != null && event.slug != null) { // This is a successful referral install _processSuccessfulReferral(event); } else { // Organic install _processOrganicInstall(event); } } static void _handleReferralLink(LinkClickEvent event) { if (event.error != null) return; final uri = Uri.parse(event.originalUrl); final referrerId = uri.queryParameters['ref']; if (referrerId != null) { // Store referral attribution _storeReferralAttribution(referrerId); } } static Future<void> _processSuccessfulReferral(AppInstallEvent event) async { try { // Notify backend of successful referral await http.post( Uri.parse('${ApiConfig.baseUrl}/api/referral-success'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({ 'referrerUsername': event.username, 'referralSlug': event.slug, 'installData': event.toJson(), }), ); // Show referral success message _showReferralSuccessMessage(event.username!); } catch (e) { print('Referral processing error: $e'); } } static Future<String?> generateReferralLink() async { try { final response = await http.post( Uri.parse('${ApiConfig.baseUrl}/api/generate-referral-link'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'userId': AuthService.currentUserId}), ); if (response.statusCode == 200) { final data = jsonDecode(response.body); return data['referralLink']; } } catch (e) { print('Referral link generation error: $e'); } return null; } static void _showReferralSuccessMessage(String referrerUsername) { Get.snackbar( 'Welcome!', 'You were referred by $referrerUsername. Thanks for joining!', backgroundColor: Colors.green, colorText: Colors.white, duration: Duration(seconds: 5), ); } }
Create UI components for sharing referral links:
class ReferralShareWidget extends StatefulWidget { @override _ReferralShareWidgetState createState() => _ReferralShareWidgetState(); } class _ReferralShareWidgetState extends State<ReferralShareWidget> { String? _referralLink; bool _isLoading = false; @override void initState() { super.initState(); _loadReferralLink(); } Future<void> _loadReferralLink() async { setState(() => _isLoading = true); _referralLink = await ReferralService.generateReferralLink(); setState(() => _isLoading = false); } @override Widget build(BuildContext context) { return Card( child: Padding( padding: EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Invite Friends', style: Theme.of(context).textTheme.headlineSmall, ), SizedBox(height: 8), Text( 'Share your referral link and earn rewards when friends join!', style: Theme.of(context).textTheme.bodyMedium, ), SizedBox(height: 16), if (_isLoading) Center(child: CircularProgressIndicator()) else if (_referralLink != null) Column( children: [ Container( padding: EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.grey[100], borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Expanded( child: Text( _referralLink!, style: TextStyle(fontFamily: 'monospace'), ), ), IconButton( icon: Icon(Icons.copy), onPressed: () { Clipboard.setData(ClipboardData(text: _referralLink!)); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Link copied to clipboard!')), ); }, ), ], ), ), SizedBox(height: 16), Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: () => _shareReferralLink(), icon: Icon(Icons.share), label: Text('Share'), ), ), SizedBox(width: 8), Expanded( child: OutlinedButton.icon( onPressed: () => _showReferralStats(), icon: Icon(Icons.analytics), label: Text('Stats'), ), ), ], ), ], ), ], ), ), ); } void _shareReferralLink() { Share.share( 'Join me on ${AppConfig.appName}! Use my referral link: ${_referralLink}', subject: 'Join ${AppConfig.appName}!', ); } void _showReferralStats() { Get.toNamed('/referral-stats'); } }
Create analytics endpoints to track referral performance:
app.get('/api/referral-analytics', async (req, res) => { try { const { userId, timeRange = '30d' } = req.query; const analytics = await getReferralAnalytics(userId, timeRange); res.json({ success: true, analytics: { totalReferrals: analytics.totalReferrals, successfulReferrals: analytics.successfulReferrals, conversionRate: analytics.conversionRate, totalRewards: analytics.totalRewards, topChannels: analytics.topChannels, dailyStats: analytics.dailyStats, viralCoefficient: analytics.viralCoefficient } }); } catch (error) { console.error('Analytics error:', error); res.status(500).json({ error: 'Failed to fetch analytics' }); } }); async function getReferralAnalytics(userId, timeRange) { const startDate = getStartDate(timeRange); const [referrals, successful, rewards, channels] = await Promise.all([ db.query('SELECT COUNT(*) as count FROM referral_attributions WHERE referrer_id = ? AND created_at >= ?', [userId, startDate]), db.query('SELECT COUNT(*) as count FROM referral_attributions WHERE referrer_id = ? AND status = "completed" AND created_at >= ?', [userId, startDate]), db.query('SELECT SUM(amount) as total FROM referral_rewards WHERE referrer_id = ? AND created_at >= ?', [userId, startDate]), db.query('SELECT channel, COUNT(*) as count FROM referral_attributions WHERE referrer_id = ? AND created_at >= ? GROUP BY channel ORDER BY count DESC', [userId, startDate]) ]); return { totalReferrals: referrals[0].count, successfulReferrals: successful[0].count, conversionRate: referrals[0].count > 0 ? (successful[0].count / referrals[0].count) * 100 : 0, totalRewards: rewards[0].total || 0, topChannels: channels, dailyStats: await getDailyReferralStats(userId, startDate), viralCoefficient: await calculateViralCoefficient(userId, startDate) }; }
You now have a complete referral program with deep linking, install attribution, and comprehensive analytics. This system will help you grow your user base organically while providing excellent user experiences.