Deferred Deep Linking
Deferred deep linking allows you to route new users to specific content after they install your app, creating a seamless experience from click to conversion.
What is Deferred Deep Linking?
Regular deep linking only works for users who already have your app installed.
Deferred deep linking works for new users who don't have your app yet:
- User clicks link to product page
- User doesn't have app → Sent to App Store/Google Play
- User installs app
- User opens app for first time
- App automatically navigates to the original product page
The "deep link" is deferred until after install.
Why It Matters
Without Deferred Deep Linking
User clicks: Instagram ad for wireless headphones
Flow:
- Click ad → App Store
- Install app
- Open app → Generic home screen
- User has to search for headphones again
- 60-70% drop-off - user forgets or gives up
With Deferred Deep Linking
User clicks: Same Instagram ad
Flow:
- Click ad → App Store
- Install app
- Open app → Automatically shows wireless headphones
- User adds to cart immediately
- 15-25% drop-off - seamless experience
Impact: 3-5x higher conversion rate from click to purchase.
How It Works
1. Link Creation
Create a link with deep link parameters:
curl -X POST https://api.linkforty.com/api/links \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"templateId": "template_123",
"originalUrl": "https://example.com/products/123?productId=123&category=electronics",
"iosUrl": "https://apps.apple.com/app/id123456789",
"androidUrl": "https://play.google.com/store/apps/details?id=com.yourapp",
"attributionWindowHours": 168
}'
Generated: https://lnk.forty.com/abc123
2. User Clicks Link
When user clicks, LinkForty:
- Detects device (iOS, Android, or Web)
- Creates fingerprint from:
- IP address
- User-agent
- Screen resolution
- Timezone
- Language
- Stores fingerprint with link data and timestamp
- Redirects to App Store (iOS) or Google Play (Android)
3. User Installs App
User downloads and installs from store.
Important: Attribution window starts when user clicks link, not when they install.
4. User Opens App
On first launch, your app calls SDK:
import { LinkFortySDK } from '@linkforty/react-native';
const deepLinkData = await LinkFortySDK.getDeepLinkData();
5. LinkForty Matches Install
SDK:
- Generates fingerprint from same device signals
- Sends to LinkForty API
- Searches for matching click within attribution window
- Returns deep link data if match found
// Returns:
{
productId: "123",
category: "electronics",
utm_source: "instagram",
utm_campaign: "spring-sale"
}
6. App Navigates
Your app uses deep link data to navigate:
if (deepLinkData && deepLinkData.productId) {
navigation.navigate('Product', { id: deepLinkData.productId });
}
User sees the original product page they clicked on!
Fingerprint Matching
What is Fingerprinting?
Fingerprinting creates a unique identifier from device characteristics without requiring:
- Device IDs (IDFA, GAID)
- User accounts
- Cookies
- App Tracking Transparency permission (iOS)
Fingerprint Components
LinkForty uses these signals:
| Signal | Example | Stability |
|---|---|---|
| IP Address | 192.168.1.1 | High |
| User-Agent | iPhone14,2; iOS 17.1 | Very High |
| Screen Resolution | 1170x2532 | Very High |
| Timezone | America/New_York | High |
| Language | en-US | High |
| Platform | iOS | Very High |
Combined, these create a fingerprint like:
fp_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
Match Accuracy
Expected accuracy: 70-80%
| Scenario | Match Rate |
|---|---|
| Same WiFi, same device, under 24h | 90-95% |
| Same cellular, same device, under 7 days | 75-85% |
| Different network (WiFi → cellular) | 60-70% |
| VPN or proxy in between | 30-50% |
| Over 14 days between click and install | 50-60% |
Why Matches Fail
Common reasons for no match:
- Network changed - Clicked on WiFi, installed on cellular
- VPN/proxy used - IP address changes
- Attribution window expired - Installed too long after click
- Different browser - In-app browser vs Safari (iOS)
- iOS Private Relay - Hides IP address
- Multiple devices - Clicked on iPad, installed on iPhone
Improving Match Rate
1. Longer attribution windows
- 7 days: 70-75% match rate
- 14 days: 75-80% match rate
- 30 days: 65-70% match rate (more false positives)
2. Platform-specific links
- Create separate links for iOS and Android
- Reduces ambiguity in matching
3. Direct traffic
- Social media → Good match rates (in-app browsers)
- SMS/iMessage → Excellent match rates (same device)
- Email → Variable (different devices)
Implementation
Step 1: Create Links
Add deep link parameters to your link URLs:
# Product link
curl -X POST https://api.linkforty.com/api/links \
-H "Authorization: Bearer $LINKFORTY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"templateId": "template_123",
"originalUrl": "https://example.com/products/headphones?productId=456&category=audio",
"iosUrl": "https://apps.apple.com/app/id123",
"androidUrl": "https://play.google.com/store/apps/details?id=com.app"
}'
Deep link parameters: Any query parameter in originalUrl becomes available in app.
Step 2: Integrate SDK
Install and initialize SDK:
import { LinkFortySDK } from '@linkforty/react-native';
// Initialize on app launch
LinkFortySDK.initialize({
apiKey: 'your-api-key'
});
See SDK Integration Guide for full setup.
Step 3: Check for Deep Link
On first app launch:
import { useEffect, useState } from 'react';
import { LinkFortySDK } from '@linkforty/react-native';
import { useNavigation } from '@react-navigation/native';
function App() {
const navigation = useNavigation();
const [isReady, setIsReady] = useState(false);
useEffect(() => {
checkDeepLink();
}, []);
const checkDeepLink = async () => {
try {
const deepLinkData = await LinkFortySDK.getDeepLinkData();
if (deepLinkData) {
console.log('Deep link data:', deepLinkData);
handleDeepLink(deepLinkData);
}
setIsReady(true);
} catch (error) {
console.error('Error checking deep link:', error);
setIsReady(true);
}
};
const handleDeepLink = (data) => {
if (data.productId) {
// Navigate to product
navigation.navigate('Product', { id: data.productId });
} else if (data.category) {
// Navigate to category
navigation.navigate('Category', { slug: data.category });
} else if (data.screen) {
// Navigate to specific screen
navigation.navigate(data.screen);
}
};
if (!isReady) {
return <LoadingScreen />;
}
return (
// Your app
);
}
Step 4: Test
Test flow:
- Create test link with query parameters
- Copy link to notes app on test device
- Uninstall app from device
- Click link from notes
- Install app from store
- Open app
- Check if navigated to correct content
Expected logs:
[LinkForty] Checking for deep link...
[LinkForty] Match found!
[LinkForty] Deep link data: {
productId: "456",
category: "audio",
utm_source: "test"
}
[App] Navigating to product 456
Use Cases
E-commerce Product Links
Link: Instagram ad for wireless headphones
{
"originalUrl": "https://shop.example.com/products/wireless-headphones?productId=789",
"iosUrl": "https://apps.apple.com/app/id123",
"androidUrl": "https://play.google.com/store/apps/details?id=com.shop"
}
App flow:
const data = await LinkFortySDK.getDeepLinkData();
// { productId: "789" }
navigation.navigate('Product', { id: data.productId });
User sees: Wireless headphones product page immediately after install.
Content Deep Links
Link: Email with article link
{
"originalUrl": "https://blog.example.com/articles/how-to-cook-pasta?articleId=123§ion=recipes",
"iosUrl": "https://apps.apple.com/app/id456",
"androidUrl": "https://play.google.com/store/apps/details?id=com.blog"
}
App flow:
const data = await LinkFortySDK.getDeepLinkData();
// { articleId: "123", section: "recipes" }
navigation.navigate('Article', { id: data.articleId });
User sees: Article about cooking pasta.
Referral Programs
Link: Friend's referral link
{
"originalUrl": "https://example.com/referral/john-doe?referrerId=user_456&reward=10",
"iosUrl": "https://apps.apple.com/app/id789",
"androidUrl": "https://play.google.com/store/apps/details?id=com.app"
}
App flow:
const data = await LinkFortySDK.getDeepLinkData();
// { referrerId: "user_456", reward: "10" }
// Award referral credit
await awardReferralCredit(data.referrerId, data.reward);
// Show success message
showToast(`You received $${data.reward} credit from ${getReferrerName(data.referrerId)}!`);
User sees: Welcome message with referral credit.
Promotional Campaigns
Link: Limited-time offer
{
"originalUrl": "https://example.com/promo/spring-sale?couponCode=SPRING20&discount=20",
"iosUrl": "https://apps.apple.com/app/id123",
"androidUrl": "https://play.google.com/store/apps/details?id=com.app"
}
App flow:
const data = await LinkFortySDK.getDeepLinkData();
// { couponCode: "SPRING20", discount: "20" }
// Auto-apply coupon
await applyCoupon(data.couponCode);
// Navigate to sale
navigation.navigate('Sale', { discount: data.discount });
User sees: Sale page with coupon already applied.
Advanced Techniques
Multi-Step Onboarding
Defer deep link until after onboarding:
const [onboardingComplete, setOnboardingComplete] = useState(false);
const [pendingDeepLink, setPendingDeepLink] = useState(null);
useEffect(() => {
checkDeepLink();
}, []);
const checkDeepLink = async () => {
const data = await LinkFortySDK.getDeepLinkData();
if (data) {
setPendingDeepLink(data);
}
};
const handleOnboardingComplete = () => {
setOnboardingComplete(true);
if (pendingDeepLink) {
handleDeepLink(pendingDeepLink);
}
};
Conditional Navigation
Navigate based on user state:
const handleDeepLink = async (data) => {
const user = await getCurrentUser();
if (!user) {
// Not logged in - show login, then navigate
navigation.navigate('Login', {
redirectTo: 'Product',
redirectParams: { id: data.productId }
});
} else {
// Logged in - navigate directly
navigation.navigate('Product', { id: data.productId });
}
};
Deep Link Fallbacks
Handle missing or invalid data:
const handleDeepLink = async (data) => {
if (data.productId) {
// Validate product exists
const product = await api.getProduct(data.productId);
if (product) {
navigation.navigate('Product', { id: data.productId });
} else {
// Invalid product - fallback to category
if (data.category) {
navigation.navigate('Category', { slug: data.category });
} else {
// No valid destination - go to home
navigation.navigate('Home');
}
}
}
};
Deep Link Analytics
Track deep link usage:
const handleDeepLink = async (data) => {
// Track deep link event
await LinkFortySDK.trackEvent({
eventName: 'deep_link_opened',
properties: {
productId: data.productId,
utm_source: data.utm_source,
utm_campaign: data.utm_campaign,
hasAccount: !!user
}
});
// Navigate
navigation.navigate('Product', { id: data.productId });
};
Troubleshooting
Deep Link Data Returns Null
Symptom: getDeepLinkData() returns null despite user clicking link.
Debug steps:
-
Check attribution window:
# View link details
curl https://api.linkforty.com/api/links/abc123 \
-H "Authorization: Bearer $LINKFORTY_API_KEY"
# Check attribution_window_hours value -
Enable debug logging:
LinkFortySDK.initialize({
apiKey: API_KEY,
enableLogging: true
}); -
Check click was recorded:
- Go to Analytics → Links → Select link
- Verify click appears in list
- Note timestamp
-
Check install timing:
- Compare click timestamp to install time
- If >attribution window → No match expected
-
Verify network conditions:
- Same WiFi/cellular for click and install?
- VPN enabled?
- iOS Private Relay active?
Low Match Rates
Expected: 70-80% for normal conditions
If lower (e.g., 40-50%):
-
Increase attribution window:
- Try 14 days instead of 7
- May capture more delayed installs
-
Check traffic source:
- In-app browsers → Good (70-80%)
- Web browsers → Lower (60-70%)
- Email links → Variable (different devices)
-
Platform-specific issues:
- iOS 14+ privacy features reduce accuracy
- Android: Check Google Play Install Referrer integration
-
Geographic factors:
- Users in regions with unstable IPs
- Frequent VPN usage
Incorrect Content Loaded
Symptom: Deep link data has wrong product ID.
Possible causes:
-
False positive match:
- User clicked different link before
- Fingerprints happened to match
- Solution: Shorter attribution window
-
Query parameter typo:
- Check link URL has correct parameters
- Verify parameter names match app code
-
Cached data:
- Old deep link data cached
- Solution: Clear app data and retest
Best Practices
1. Always Provide Deep Link Parameters
Include useful data in every link:
✅ Good:
originalUrl: "https://example.com/product?productId=123&category=electronics"
❌ Bad:
originalUrl: "https://example.com/product"
2. Use Meaningful Parameter Names
Be explicit:
✅ Good:
?productId=123&categorySlug=electronics
❌ Bad:
?id=123&cat=elec
3. Handle Missing Parameters
Always provide fallbacks:
const data = await LinkFortySDK.getDeepLinkData();
if (data?.productId) {
navigation.navigate('Product', { id: data.productId });
} else if (data?.category) {
navigation.navigate('Category', { slug: data.category });
} else {
navigation.navigate('Home');
}
4. Test on Real Devices
Fingerprint matching doesn't work in simulators. Always test on physical devices.
5. Set Appropriate Attribution Windows
Match window to expected user behavior:
- Impulse products: 1-3 days
- Considered purchases: 7-14 days
- Referrals: 30-90 days
6. Track Deep Link Performance
Monitor conversion rates:
// Track when deep link opens
await LinkFortySDK.trackEvent({
eventName: 'deep_link_opened',
properties: { productId: data.productId }
});
// Track if user converts
await LinkFortySDK.trackEvent({
eventName: 'purchase',
value: 29.99,
properties: { productId: data.productId }
});
View in dashboard: See conversion rate from deep link open → purchase.
Next Steps
- 🔧 SDK Integration - Set up mobile SDK
- 📱 React Native SDK - Complete React Native guide
- 🎯 Attribution Windows - Configure windows
- 🔗 Creating Links - Create deep links
- 📊 Analytics - Monitor performance
Further Reading
- Fingerprint Matching - Technical deep dive
- Mobile Attribution - Attribution concepts
- QR Codes - Offline-to-online deep linking