Android Deferred Deep Links
Preserve a link's destination through a Play Store install, then route the user on first launch, with the WarpLink Android SDK.
Deferred deep links let you route users to specific content even when they don't have your app installed. The user clicks a link, installs from the Play Store, and on first launch the SDK matches them back to the original link.
How It Works
- User taps a WarpLink URL in Chrome
- WarpLink captures browser signals and sets the Play Install Referrer with
utm_source=warplink&utm_content={link_id} - User is redirected to the Play Store
- User installs and opens the app
- SDK detects first launch. A completion marker in the app's no-backup storage (
noBackupFilesDir) is written only after the check completes, and Auto Backup never restores it, so a reinstall attributes fresh. If that directory cannot be written, the marker falls back to a SharedPreferences entry bound to the install, so a restore cannot carry it onto a reinstall. - SDK reads the Play Install Referrer. If it contains
utm_source=warplink, a deterministic match is made (confidence 1.0) - If the referrer is unavailable, the SDK falls back to probabilistic matching (device language + timezone, with the IP derived server-side). Clicks that share a match key are kept as a list, newest first, up to 10, so a second click never overwrites the first
- Server returns a
WarpLinkDeepLinkwithisDeferred = true
Automatic Check
The deferred check fires automatically from configure(). The result arrives in your onLink callback, where isDeferred is true:
WarpLink.configure(
context = this,
apiKey = "wl_live_yoursdkkeyhere000000000000000000",
options = WarpLinkOptions(onLink = { result ->
result.onSuccess { deepLink ->
if (deepLink.isDeferred) {
val confidence = deepLink.matchConfidence ?: 0.0
if (confidence > 0.5) {
navigateTo(deepLink.deepLinkUrl ?: deepLink.destination)
} else {
showWelcome(suggestedContent = deepLink.destination)
}
} else {
navigateTo(deepLink.destination) // a tap, not a deferred install
}
}
})
)
If that check cannot reach the server, for example on a launch with no network, the failure also arrives in onLink. The first-launch gate stays open, so the next launch retries by itself. Treat a WarpLinkError.NetworkError there as a report, not as something to act on.
Manual Check (Advanced)
To run the check yourself, set automaticDeferredDeepLinks = false in WarpLinkOptions and call checkDeferredDeepLink:
WarpLink.checkDeferredDeepLink { result ->
result.onSuccess { deepLink ->
if (deepLink == null) {
showOnboarding()
return@onSuccess
}
val confidence = deepLink.matchConfidence ?: 0.0
if (confidence > 0.5) {
val target = deepLink.deepLinkUrl ?: deepLink.destination
navigateTo(target)
} else {
showWelcome(suggestedContent = deepLink.destination)
}
}.onFailure { error ->
Log.e("MyApp", "Deferred deep link error: ${error.message}")
showOnboarding()
}
}
Confidence-Based Routing
WarpLink.checkDeferredDeepLink { result ->
result.onSuccess { deepLink ->
if (deepLink == null || !deepLink.isDeferred) return@onSuccess
val confidence = deepLink.matchConfidence ?: 0.0
if (confidence > 0.5) {
// High confidence: navigate straight to the link's destination
navigateTo(deepLink.deepLinkUrl ?: deepLink.destination)
} else if (confidence > 0.3) {
// Medium confidence: show suggestion
showWelcome(suggestedContent = deepLink.destination)
}
// Below 0.3: ignore, show default onboarding
}
}
Confidence Scores
Probabilistic scores depend on the match key variant. enriched_tz hashes the IANA timezone name and is what an SDK that collects a zone name sends. enriched hashes the minute offset instead and stays as the fallback for older SDKs. basic drops the timezone entirely.
| Scenario | Confidence | Match Type |
|---|---|---|
| Play Install Referrer match | 1.0 | DETERMINISTIC |
enriched_tz match key, < 1 hour | 0.85 | PROBABILISTIC |
enriched_tz match key, < 3 hours | 0.65 | PROBABILISTIC |
enriched_tz match key, < 6 hours | 0.50 | PROBABILISTIC |
enriched_tz match key, < 24 hours | 0.30 | PROBABILISTIC |
The offset variant scores 0.80 / 0.60 / 0.45 / 0.25 across the same bands, and basic scores 0.70 / 0.50 / 0.35 / 0.20. Two further signals adjust the score: more than one distinct link in the match key bucket applies x0.6, and a shared click IP applies x0.6 for carrier-grade NAT or a private address, x0.9 for a household IPv4 address, and x1.0 for IPv6. Past 24 hours there is no match at all.
Recommendation: Route to specific content when matchConfidence > 0.5. Show generic onboarding below 0.5. matchGuaranteed is true for a deterministic match (the Play Install Referrer) and sharpens the routing choice further. Route a probabilistic match to content or a sign-in screen, and keep sign-in on your own authentication.
Match Window
The match window is set per link on the server, not in the SDK. Configure it in the dashboard when you create or edit a link. The default is 6 hours and the maximum is 24 hours.
The window is deliberately short so the match key bucket stays tight and matches stay precise. Links created before the current limits may still carry a longer stored value, but the server caps every window at 24 hours when it reads them.
This governs probabilistic matching only. The Play Install Referrer branch is deterministic and is not affected by the window.
Caching Behavior
- The attribution check runs on first launch
- Once it completes, a completion marker in the app's no-backup storage (
noBackupFilesDir) is recorded so it does not run again for this install - Subsequent launches return without another network request
- An attempt that produced no usable answer (offline, or a match the SDK cannot route to a destination) is not recorded as complete, so it retries on the next launch
- A reinstall is a new install, so it gets a new check. See App Reinstall
Edge Cases
Play Install Referrer Unavailable
The referrer is the primary matching method but is unavailable when:
- The app is sideloaded (
adb installor direct APK) - No Google Play Services (e.g., Huawei HMS devices)
- Referrer data has expired
The SDK automatically falls back to probabilistic matching. No code changes needed.
Offline First Launch
If the device has no connectivity on first launch, checkDeferredDeepLink fails with WarpLinkError.NetworkError. The attempt is not consumed, so the SDK retries the check on the next launch.
App Reinstall
A reinstall is a new install and is attributed again. The SDK keeps two markers with two different jobs:
- Completion marker: a file in the app's no-backup storage (
noBackupFilesDir). It says the check already ran for this install. Auto Backup never captures that directory, so a reinstall starts from a clean marker - Device-seen marker: a
SharedPreferencesentry. It says the device was attributed at some point, and Auto Backup does restore it. It gates nothing. Its only job is to setis_reinstall: trueon the attribution request, so the install is recorded as a reinstall rather than a first install
Both installs count. The dashboard shows installs and reinstalls together as one installs number. No manifest or backup_rules.xml changes are required on your side: keep Auto Backup on and the two markers behave as described.
Routing-only mode
If the app is in routing-only mode, the server skips the probabilistic match. An install that carries a Play Install Referrer still matches. No SDK change is needed.
Multiple Links Before Install
Clicks that share a match key are kept as a list, newest first, up to 10. The most recent click your app can claim is the one matched, and the others stay available for the other devices that share the address. When more than one click is claimable, the confidence score is multiplied by 0.6 to report that ambiguity.
Referrer Age
The Play Install Referrer stays valid for months, so a check that runs long after the install, a 1.0.x upgrade, or the first release you ship with the SDK, still names the original link. The server still records that attribution. The SDK routes a match whose Play install began within 7 days of the check, which covers fresh installs and any check that runs soon after the Play install.
Testing
- Uninstall the app from the test device
- Open the test link in Chrome. It redirects to the Play Store (or fallback URL)
- Install via Android Studio or
adb install - Launch the app.
checkDeferredDeepLinkshould return the matched deep link
Android Deep Links
Handle Android App Links to route taps into the right screen, with cold start and warm start covered by the WarpLink SDK.
Android Install Attribution
Understand how the WarpLink Android SDK attributes installs using the Play Install Referrer, probabilistic matching, and privacy controls.