Skip to main content

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:

  1. User clicks link to product page
  2. User doesn't have app → Sent to App Store/Google Play
  3. User installs app
  4. User opens app for first time
  5. 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:

  1. Click ad → App Store
  2. Install app
  3. Open app → Generic home screen
  4. User has to search for headphones again
  5. 60-70% drop-off - user forgets or gives up

With Deferred Deep Linking

User clicks: Same Instagram ad

Flow:

  1. Click ad → App Store
  2. Install app
  3. Open app → Automatically shows wireless headphones
  4. User adds to cart immediately
  5. 15-25% drop-off - seamless experience

Impact: 3-5x higher conversion rate from click to purchase.

How It Works

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

When user clicks, LinkForty:

  1. Detects device (iOS, Android, or Web)
  2. Creates fingerprint from:
    • IP address
    • User-agent
    • Screen resolution
    • Timezone
    • Language
  3. Stores fingerprint with link data and timestamp
  4. 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:

  1. Generates fingerprint from same device signals
  2. Sends to LinkForty API
  3. Searches for matching click within attribution window
  4. 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:

SignalExampleStability
IP Address192.168.1.1High
User-AgentiPhone14,2; iOS 17.1Very High
Screen Resolution1170x2532Very High
TimezoneAmerica/New_YorkHigh
Languageen-USHigh
PlatformiOSVery High

Combined, these create a fingerprint like:

fp_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6

Match Accuracy

Expected accuracy: 70-80%

ScenarioMatch Rate
Same WiFi, same device, under 24h90-95%
Same cellular, same device, under 7 days75-85%
Different network (WiFi → cellular)60-70%
VPN or proxy in between30-50%
Over 14 days between click and install50-60%

Why Matches Fail

Common reasons for no match:

  1. Network changed - Clicked on WiFi, installed on cellular
  2. VPN/proxy used - IP address changes
  3. Attribution window expired - Installed too long after click
  4. Different browser - In-app browser vs Safari (iOS)
  5. iOS Private Relay - Hides IP address
  6. 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

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.

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:

  1. Create test link with query parameters
  2. Copy link to notes app on test device
  3. Uninstall app from device
  4. Click link from notes
  5. Install app from store
  6. Open app
  7. 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

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.

Link: Email with article link

{
"originalUrl": "https://blog.example.com/articles/how-to-cook-pasta?articleId=123&section=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 });
}
};

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');
}
}
}
};

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

Symptom: getDeepLinkData() returns null despite user clicking link.

Debug steps:

  1. 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
  2. Enable debug logging:

    LinkFortySDK.initialize({
    apiKey: API_KEY,
    enableLogging: true
    });
  3. Check click was recorded:

    • Go to Analytics → Links → Select link
    • Verify click appears in list
    • Note timestamp
  4. Check install timing:

    • Compare click timestamp to install time
    • If >attribution window → No match expected
  5. 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%):

  1. Increase attribution window:

    • Try 14 days instead of 7
    • May capture more delayed installs
  2. Check traffic source:

    • In-app browsers → Good (70-80%)
    • Web browsers → Lower (60-70%)
    • Email links → Variable (different devices)
  3. Platform-specific issues:

    • iOS 14+ privacy features reduce accuracy
    • Android: Check Google Play Install Referrer integration
  4. Geographic factors:

    • Users in regions with unstable IPs
    • Frequent VPN usage

Incorrect Content Loaded

Symptom: Deep link data has wrong product ID.

Possible causes:

  1. False positive match:

    • User clicked different link before
    • Fingerprints happened to match
    • Solution: Shorter attribution window
  2. Query parameter typo:

    • Check link URL has correct parameters
    • Verify parameter names match app code
  3. Cached data:

    • Old deep link data cached
    • Solution: Clear app data and retest

Best Practices

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

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

Further Reading