You've set up your assetlinks.json, configured your intent filters, and published your app. A user installs it. They tap a link to your domain. Instead of your app opening directly, they see the disambiguation dialog: "Open with Chrome or YourApp?"
The App Links verification hasn't completed yet. It can take anywhere from seconds to hours after installation, depending on network conditions, server response times, and the Android version. During this window, your App Links behave like unverified deep links.
This guide covers why verification delays happen, what the user experience looks like during the delay, and strategies to minimize the impact. For the verification process itself, see how Android App Link verification works. For debugging verification failures, see the debugging intent resolution guide.
Why Verification Takes Time
The Verification Flow
When an app is installed (or updated), Android's verification service:
- Parses the app's manifest for intent filters with
android:autoVerify="true". - Extracts all declared
android:hostvalues. - Makes HTTPS requests to
https://{host}/.well-known/assetlinks.jsonfor each host. - Validates the response: correct JSON structure, matching package name, matching SHA-256 fingerprint.
- Records the verification status for each domain.
Each of these steps can introduce delays.
Common Delay Causes
Network conditions. The verification request is made by the Android system service, which may be queued behind other tasks. On slow or metered connections, the request may be deprioritized or time out.
Server response time. If your assetlinks.json takes more than 5 seconds to respond, Android may treat it as a failure. Slow CDNs, cold starts on serverless endpoints, or overloaded servers all contribute.
Batch processing. Android doesn't verify each app immediately on install. Verification requests are batched and processed periodically. On some devices, this batch runs every few minutes; on others, it may be deferred to a maintenance window.
Multiple domains. Each domain is verified with a separate HTTP request. If you have 5 domains in your intent filters, that's 5 requests. A timeout on any one can delay the entire verification.
Device state. If the device is in Doze mode, battery saver, or has restricted background data, verification requests may be deferred.
What Happens During the Delay
Before Verification Completes
- Android 12+: Unverified App Links open in the browser by default. The user doesn't see a disambiguation dialog; the link just opens in Chrome. Your app is effectively invisible for those links.
- Android 11 and below: Unverified links show the "Open with" disambiguation dialog. The user can choose your app or the browser.
After Verification Completes
Links open directly in your app with no dialog or browser involvement.
Verification Persistence
Once verified, the status persists until:
- The app is uninstalled and reinstalled.
- The app is updated (triggers re-verification on some Android versions).
- The user manually resets App Link preferences in settings.
- The system periodically re-verifies (Android 14+).
Strategies for Handling Delays
Strategy 1: Optimize Server Response Time
The fastest fix. Make your assetlinks.json response as fast as possible:
# Nginx: serve assetlinks.json from memory with long cache
location = /.well-known/assetlinks.json {
alias /etc/assetlinks/assetlinks.json;
default_type application/json;
add_header Cache-Control "public, max-age=3600";
# Enable gzip for faster transfer
gzip on;
gzip_types application/json;
}
Target: under 200ms response time. The file is tiny (typically under 500 bytes), so the bottleneck is usually connection establishment, not transfer.
CDN caching: Serve the file through a CDN (Cloudflare, CloudFront) with edge caching enabled. This puts the file physically closer to the Android verification service.
No redirects: The request to /.well-known/assetlinks.json must return a direct 200 response. Redirects (301, 302) may not be followed by the verification service on all Android versions.
Strategy 2: Use a Web Fallback That Redirects to the App
For the critical first-open experience, don't rely solely on App Links. Use a web page that detects the app and redirects:
<!-- Your landing page at links.yourapp.com/welcome -->
<html>
<head>
<meta http-equiv="refresh" content="2;url=https://play.google.com/store/apps/details?id=com.example.app">
</head>
<body>
<script>
// Try to open the app via intent URL
const intentUrl = 'intent://welcome#Intent;' +
'scheme=https;' +
'package=com.example.app;' +
'S.browser_fallback_url=' + encodeURIComponent(window.location.href) + ';' +
'end';
window.location.href = intentUrl;
</script>
<p>Opening app... <a href="https://play.google.com/store/apps/details?id=com.example.app">
Download from Play Store
</a></p>
</body>
</html>
If verification hasn't completed and the link opens in the browser, the page attempts to open the app via an intent URL (which doesn't require verification). If the app isn't installed, it falls back to the Play Store.
Strategy 3: Use Tolinku Smart Links
Tolinku deep links handle verification delays transparently. When a user taps a Tolinku link:
- If App Links verification is complete, the app opens directly.
- If verification is pending, the Tolinku redirect chain detects the platform and attempts to open the app through alternative mechanisms (intent URLs, JavaScript redirect).
- If the app isn't installed, the user is sent to the app store or a web fallback.
This three-layer approach ensures the link works regardless of verification status.
Strategy 4: Test Verification Before Relying on It
In your app's first-run experience, check whether App Links are verified:
// Check if App Links are verified for your domain
fun checkAppLinkStatus(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val manager = context.getSystemService(DomainVerificationManager::class.java)
val userState = manager.getDomainVerificationUserState(context.packageName)
val verifiedDomains = userState?.hostToStateMap
?.filter { it.value == DomainVerificationUserState.DOMAIN_STATE_VERIFIED }
?.keys ?: emptySet()
val unverifiedDomains = userState?.hostToStateMap
?.filter { it.value != DomainVerificationUserState.DOMAIN_STATE_VERIFIED }
?.keys ?: emptySet()
if (unverifiedDomains.isNotEmpty()) {
// Verification pending or failed for some domains
// Consider prompting user to set default app manually
Log.w("AppLinks", "Unverified domains: $unverifiedDomains")
}
}
}
Strategy 5: Prompt Users to Set Default App
If verification fails or is delayed, you can guide users to manually approve your app as the default handler:
// Android 12+: Open the app's default links settings
fun promptDefaultApp(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val intent = Intent(
Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS,
Uri.parse("package:${context.packageName}")
)
context.startActivity(intent)
}
}
Use this as a last resort. Most users won't navigate system settings, but power users and testers will appreciate the option.
Monitoring Verification in Production
Server-Side Monitoring
Monitor requests to your assetlinks.json endpoint:
# Expected pattern: requests from Android verification service
User-Agent: Mozilla/5.0 (Linux; Android *)
Track:
- Request volume: Spikes after app releases indicate re-verification.
- Response codes: Any 4xx or 5xx responses mean failed verifications.
- Response time: Responses over 5 seconds may timeout.
Client-Side Monitoring
On Android 12+, use the DomainVerificationManager API to check verification status and report it to your analytics:
fun reportVerificationStatus(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val manager = context.getSystemService(DomainVerificationManager::class.java)
val userState = manager.getDomainVerificationUserState(context.packageName)
userState?.hostToStateMap?.forEach { (domain, state) ->
val statusName = when (state) {
DomainVerificationUserState.DOMAIN_STATE_VERIFIED -> "verified"
DomainVerificationUserState.DOMAIN_STATE_SELECTED -> "selected"
DomainVerificationUserState.DOMAIN_STATE_NONE -> "none"
else -> "unknown"
}
analytics.trackEvent("app_link_status", mapOf(
"domain" to domain,
"status" to statusName
))
}
}
}
This gives you visibility into what percentage of your user base has verified App Links.
Timing Expectations
| Scenario | Typical Verification Time |
|---|---|
| Fresh install, good network | 10-60 seconds |
| Fresh install, slow network | 1-5 minutes |
| Fresh install, server timeout | Never (fails) |
| App update | 10-60 seconds (re-verification) |
| Device reboot | May re-verify within minutes |
| Android 14 periodic re-check | Every few days |
The bottom line: don't assume verification is instant. Design your link flows to work even when verification hasn't completed.
For the full App Links setup, see the Android App Links complete guide. For Tolinku's approach to handling verification across domains, the platform manages asset links files with optimized caching and CDN delivery.
Get deep linking tips in your inbox
One email per week. No spam.