Android App Links: The Complete Setup Guide (2026)
Android App Links explained: how the assetlinks.json handshake works, step-by-step setup with autoVerify, common verification failures, adb debugging, and a no-code option.
Android App Links are standard HTTPS URLs that open your Android app when installed and fall back to the browser when not. Android verifies the link through a Digital Asset Links handshake: your app declares intent filters with android:autoVerify="true", and each domain hosts an assetlinks.json file at /.well-known/assetlinks.json that lists the SHA-256 signing fingerprints of the apps allowed to open its URLs.
Android App Links have one of the most frustrating debugging profiles in mobile development. When they work, they feel like magic - a tap on a normal web URL launches your app directly to the right screen with no chooser sheet, no browser flash, no error. When they don't work, they silently fall back to whichever browser the user has set as default and give you nothing to grep for. No log line, no crash report, no user-facing hint. Just a link that goes to the wrong place.
This guide covers everything you actually need to ship App Links in 2026: what they are, how the verification handshake works underneath, how to set one up correctly in AndroidManifest and on your server, how to debug the classic silent failures with adb, and where a no-code alternative fits. It is written for Android engineers, but the debugging section is useful even if you outsourced the initial implementation to a library or SDK.
Table of Contents
- What Are Android App Links?
- The Digital Asset Links Handshake
- Deep Links vs Web Links vs App Links
- How to Set Up Android App Links Step by Step
- The assetlinks.json File Explained
- Debugging App Links with adb
- Common Failures and How to Fix Them
- What Changed in Android 12+
- When App Links Alone Are Not Enough
- The No-Code Alternative: Skip the Setup
- Frequently Asked Questions
What Are Android App Links?
An Android App Link is an HTTPS web link that opens content inside an Android app when installed and falls back to the browser when not. App Links are verified against a domain the developer controls through a Digital Asset Links assetlinks.json file, and against the app itself through an intent filter that carries the android:autoVerify="true" attribute. Introduced in Android 6.0 (Marshmallow), they are Google's official way to skip the "Open with..." chooser for links you own.
The single-sentence pitch: one HTTPS URL, two graceful behaviors on Android. Tap it with your app installed and Android silently launches the app to the exact screen you routed. Tap it without your app and the URL loads in the user's default browser. Same URL, different behavior depending on the device, no error path, no chooser dialog.
App Links solve three problems that plagued the older Android deep linking model. First, they get rid of the "Complete action using" chooser sheet that used to appear every time a URL matched more than one app. Second, they add cryptographic proof that your app is actually allowed to claim the domain, so nobody can register the same intent filter and steal your traffic. Third, they degrade gracefully to the web when the app is missing, instead of the URI-scheme model's silent failures.
If you have shipped iOS Universal Links before, the mental model is nearly identical. Both are HTTPS URLs claimed by an app and verified against a JSON file on the developer's domain. The differences are cosmetic: Android uses assetlinks.json instead of apple-app-site-association, verifies through Google's Digital Asset Links protocol instead of Apple's Associated Domains flow, and identifies the app through a SHA-256 certificate fingerprint instead of an App ID prefix. Same handshake, two ecosystems.
The Digital Asset Links Handshake
The reason App Links feel fragile is that they only work when two totally independent parties agree, ahead of time, that they belong together. Your app claims certain domains. Each domain claims certain apps. Android only routes the URL to the app when both sides of the claim match exactly.
Side one: the app. In your AndroidManifest.xml, you add one or more intent filters that carry android:autoVerify="true" and match the HTTPS URLs of your domain. That attribute is the app's public statement: "I want Android to verify me against these hosts."
Side two: the domain. On your web server, you host a JSON file at exactly https://<yourdomain>/.well-known/assetlinks.json. Served over HTTPS with no redirects, Content-Type: application/json. Inside, you list the SHA-256 fingerprints of the signing keys of the apps allowed to claim your domain. This is the domain's public statement: "These apps are me."
When the app is installed on Android 6.0 or higher, the OS reads the manifest, collects every unique host from the verified intent filters, fetches the corresponding assetlinks.json, and compares the SHA-256 fingerprints to the certificate your app was signed with. If everything matches, Android marks the domain as "verified" for your app and every future link to that host opens the app instantly. If anything fails, the domain stays unverified and your app is just one of many that could handle the link.
Google's Verify App Links documentation is the canonical reference for the verification lifecycle. The 20-second delay it mentions is real: verification is asynchronous, so a fresh install may take a beat before links start opening the app.
Deep Links vs Web Links vs App Links
Android's terminology overlaps enough to trip up seasoned developers. Getting the vocabulary straight makes every later section easier.
| Term | URL format | Opens app when installed | Opens browser when not | Verified against domain | Skips chooser |
|---|---|---|---|---|---|
| Deep Link (URI scheme) | myapp://path |
Yes (if scheme registered) | No | No | No, if scheme conflicts |
| Web Link | https://example.com/path (no autoVerify) |
Yes, but with chooser | Yes | No | No |
| Android App Link | https://example.com/path (with autoVerify) |
Yes, silently | Yes | Yes | Yes |
A deep link is any URL that opens specific content inside a mobile app. The word covers all three of the categories above, which is half the confusion. In the narrow, older sense, "deep link" usually means the custom URI scheme style (myapp://), because those were the original way to deep link on Android.
A web link is a plain HTTPS deep link. It works, but without verification the user gets the "Open with..." chooser every time, which nobody enjoys.
An Android App Link is a web link with autoVerify="true" and a matching assetlinks.json on the domain. That combination is what unlocks the chooser-less behavior. For a broader landscape look, our deep link vs universal link explainer covers where each of these terms fits across iOS and Android.
How to Set Up Android App Links Step by Step
Here is the shortest correct path to a working App Link. Every step matters. Skipping any of them produces a silent failure that looks identical to the others.
Step 1: Add App Link intent filters to AndroidManifest.xml
Open the <activity> in your manifest that should handle the incoming URL. Add an intent filter with the VIEW action, DEFAULT and BROWSABLE categories, https scheme, your host, and android:autoVerify="true":
<activity android:name=".DeepLinkActivity" android:exported="true">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="example.com" />
<data android:scheme="https" android:host="www.example.com" />
</intent-filter>
</activity>
If you handle multiple hosts (root domain plus www, or subdomains), list each one. If you only cover certain paths, add a pathPrefix, pathPattern, or path attribute to the <data> element. Missing android:autoVerify="true" is the single most common mistake in this whole process, and it turns your App Link into a plain web link.
Step 2: Get your SHA-256 signing fingerprint
Every APK is signed with a certificate, and Android verifies you by that certificate's SHA-256 fingerprint. Get it from your keystore:
keytool -list -v -keystore my-release-key.keystore -alias my-alias
If you use Play App Signing (the default for new apps), the fingerprint you need is the one Google uses to re-sign your app, not your upload key. Find it in Play Console under Setup → App integrity → App signing key certificate. Copy the SHA-256 fingerprint in uppercase hex, colon-separated.
Step 3: Create the assetlinks.json file
Publish this JSON at exactly https://<yourdomain>/.well-known/assetlinks.json. application/json content type, HTTPS with a valid cert, no redirects, no auth wall.
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.myapp",
"sha256_cert_fingerprints": [
"01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF"
]
}
}
]
If your app is used across multiple flavors or signed differently for beta vs production, list every fingerprint in the sha256_cert_fingerprints array. Google's Configure website associations documentation walks through the file format in detail.
Step 4: Verify the file is reachable
Before you install the app, sanity-check the file with curl:
curl -I https://example.com/.well-known/assetlinks.json
Confirm a raw 200, no 301/302, correct Content-Type, and that the response body is the JSON you expect. If curl works from your laptop but the file is behind a VPN or a corporate firewall, Google's crawlers cannot reach it and verification will fail silently.
Step 5: Handle the incoming URL in your app
When Android launches your Activity from an App Link, it delivers an Intent with ACTION_VIEW and the URL in getData(). In Kotlin:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val uri = intent?.data ?: return
routeToScreen(uri.path, uri.queryParameterMap())
}
Route to the right destination in-app. If the URL matches something you did not expect, gracefully fall back to the browser or your home screen instead of crashing.
Step 6: Install and let verification run
Uninstall any existing build and install a fresh copy. Wait 20 seconds for the asynchronous verification to complete. Test with a URL that matches one of your intent filter hosts.
The assetlinks.json File Explained
This file is where the vast majority of App Link problems live. It looks tiny, but every field matters.
Location. Exactly https://<yourdomain>/.well-known/assetlinks.json. This path is normative, not a suggestion. Any other location is invisible to Android's verifier.
Content type. Must be application/json. Not text/plain, not text/json. Nginx and older Apache configs often serve unknown extensions as text/plain by default, which silently kills verification.
No redirects. The URL must return 200 OK directly. HTTP-to-HTTPS redirects, apex-to-www redirects, and CDN redirects all break verification. Cloudflare's "Always Use HTTPS" is a frequent culprit; either exclude /.well-known/assetlinks.json or serve HTTPS at the redirect target directly.
HTTPS with a valid, non-self-signed certificate. Expired certs, chain issues, and self-signed certs all break verification silently.
No auth wall or IP restriction. Google's verifier requests the file from the public internet. If it is behind a VPN, a WAF rule, or basic auth, verification will fail with no on-device hint.
SHA-256 fingerprints in uppercase, colon-separated. The sha256_cert_fingerprints array must contain hex strings in the exact format Android expects. Lowercase and no colons will both fail.
Every host you claim in the manifest must have its own file. If your app claims both example.com and www.example.com, both hosts need /.well-known/assetlinks.json served independently (or served consistently for both through a single infrastructure). A subdomain does not inherit from the apex domain automatically.
Google publishes a public test tool at https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://example.com&relation=delegate_permission/common.handle_all_urls that returns whatever the Digital Asset Links API sees on your domain. If your file looks perfect but the API returns nothing, the problem is on the server or the CDN.
Debugging App Links with adb
Google's Verify App Links documentation exposes an adb interface for inspecting verification state, and it is the single most useful App Link debugging tool that exists.
Inspect verification state per package:
adb shell pm get-app-links --user cur com.example.myapp
The output lists every host declared in the app's intent filters, the verification state for each (verified, unverified, or unknown), and the timestamp of the last verification attempt. If a host you expected shows unverified, verification failed and the reason is almost always in the assetlinks.json response.
Force a re-verification:
adb shell pm verify-app-links --re-verify com.example.myapp
This is the key command for iteration. It re-fetches assetlinks.json for every host in the manifest and re-runs the verification. Use it after every fix to the JSON file. Without it, Android will happily keep the old cached failure indefinitely.
Reset verification state:
adb shell pm set-app-links --package com.example.myapp 0 all
Wipes verification state entirely so the next verification runs from scratch. Useful when you have been iterating and want a clean baseline.
Test a specific link:
adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "https://example.com/product/123"
Simulates a user tapping the URL. If your app opens directly, verification is working. If Android shows a chooser or opens the browser, the domain is not verified and you have work to do.
Common Failures and How to Fix Them
The debugging playbook, ordered from most common to least.
autoVerify="true" missing. The intent filter matches the URL but Android never attempts verification. Fix: add android:autoVerify="true" to the <intent-filter> element. This is the single most common mistake.
SHA-256 fingerprint mismatch. The build you installed is signed with a different key than the one you put in assetlinks.json. Debug builds, release builds, and Play App Signing all produce different fingerprints. Fix: include every fingerprint you actually ship with in the JSON array, in uppercase colon-separated hex.
assetlinks.json has the wrong content type. Nginx and older Apache serve unknown extensions as text/plain. Fix: explicitly map /.well-known/assetlinks.json to application/json in your server config.
Redirects on the assetlinks.json URL. Cloudflare "Always Use HTTPS," WWW-to-apex, and CMS-level redirects all break verification. Fix: confirm a raw 200 with curl -I and remove the redirect chain.
Host mismatch between manifest and JSON. You declared applinks:example.com in the app but hosted assetlinks.json only at www.example.com. Fix: host the file on every host you claim, or claim only hosts the file is on.
Missing <data> elements in the intent filter. Multiple <data> entries are ANDed, not ORed, in older Android versions, which surprises people. Fix: use separate intent filters per host, or read the Android intent filter documentation carefully.
Testing from Chrome's URL bar. Some Android versions treat direct URL bar entries as intentional browser navigation and skip the App Link handoff. Test from Messages, Gmail, or another app.
Development VPN. If your laptop is on a corporate VPN, curl succeeds while Google's public verifier fails. Test the assetlinks.json URL from a non-VPN connection.
Stale verification cache. Android caches verification aggressively. Fix: force re-verification with adb shell pm verify-app-links --re-verify <package>.
User overrode the default handler. From Android 12 onward, the user has a UI to manually change which app opens verified links. If your app is verified but still not opening, check Settings → Apps → your app → Open by default.
Google's Troubleshoot App Links documentation is worth bookmarking for the failures we did not list here.
What Changed in Android 12+
Android 12 (API level 31) tightened App Link verification in a way that broke a lot of previously-working apps. The change: HTTP web intents without autoVerify no longer open your app automatically. If you want to handle https:// links, you either configure App Link verification correctly and get it working, or the user has to manually enable your app as a link handler in Settings.
The practical impact: apps that had been getting away with unverified web link intent filters started silently sending users to the browser after users updated to Android 12. If you shipped App Links before 2022 and never came back to them, this is worth a fresh pass.
The Play Console App Links verification report surfaces which of your hosts are verifying successfully in production. It is worth checking after every release, because a server-side change on your domain can silently break App Links across your installed base.
When App Links Alone Are Not Enough
App Links solve Android. They do not solve iOS (where you need Universal Links), they do not solve deferred deep linking (opening the correct in-app screen after a fresh install from an ad), and they do not solve in-app browser routing (Instagram and Facebook can trap taps inside their webview so the URL never reaches Android's routing layer).
A production-quality deep link system usually needs all of the following:
- Android App Links for
https://links on Android. - iOS Universal Links for the same URLs on iPhone.
- A web fallback for desktop and older devices.
- Deferred deep linking so post-install routing works.
- In-app browser detection so links tapped from Instagram or Facebook escape the webview and actually trigger the App Link.
Wiring these together yourself is doable, but it is a project. Our complete deep linking guide walks through the full stack in one place, and our Firebase Dynamic Links alternative covers the migration many teams are still working through since Google shut FDL down.
The No-Code Alternative: Skip the Setup
The people searching "android app links" break down roughly two ways. About half are Android engineers who need to run through the setup and debugging above. The other half want a link that opens their app on Android without them ever touching an assetlinks.json file. If you are in the second group, there is a much shorter path.
U2L AI's URL shortener generates a single short link that behaves as an App Link on Android, a Universal Link on iOS, and a normal web URL everywhere else. You paste your destination (a YouTube video, an Amazon listing, a Spotify track, an Instagram post, or a page in your own app) and U2L AI returns a link that routes each platform through the right mechanism. No assetlinks.json to host, no signing fingerprint to look up, no verification cache to fight. The supported deep links page lists the apps covered natively.
A few things we built in because we got tired of debugging them elsewhere:
- In-app browser detection. Taps from Instagram, Facebook, TikTok, and LinkedIn get pushed out of the webview into the real app, so the App Link actually fires. Our piece on why links open in an in-app browser explains why this matters for conversion.
- Global edge redirects. The link runs on a network with 330+ locations, so the handoff to the app feels native rather than laggy.
- Analytics on every click. Country, device, OS, browser, referrer. Raw App Links have no tracking at all, and bolting it on later is a rewrite.
Ship your own App Link when you have a good reason to control the AASA on your domain (large team, regulated app, complex path matching). Reach for a tool when you just need a working link. For a broader landscape view, our roundup of the best deep link generators compares the modern options.
Frequently Asked Questions
What is the difference between Android App Links and deep links?
A deep link is any URL that opens content inside a mobile app. An Android App Link is a specific, verified flavor of deep link that uses an HTTPS URL and skips the "Open with..." chooser. Every App Link is a deep link, but not every deep link is an App Link. The older URI scheme style (myapp://) is also a deep link and does not require verification.
How do I verify Android App Links are working?
Run adb shell pm get-app-links --user cur com.example.myapp while a device is connected. The output lists every host from your intent filters and shows the verification state. Any host that shows verified will open your app directly; anything else will not.
Where does the assetlinks.json file go?
At exactly https://<yourdomain>/.well-known/assetlinks.json, served over HTTPS with Content-Type: application/json, with no redirects, and reachable from the public internet. Google's Configure website associations documentation is the authoritative reference.
Why isn't my App Link opening the app?
The most common reasons are: android:autoVerify="true" is missing from the intent filter, the SHA-256 fingerprint in assetlinks.json does not match the certificate the installed build was signed with, the file is served with the wrong content type, the URL redirects, or the verification cache is stale. Run adb shell pm verify-app-links --re-verify <package> to force a fresh verification and check the result with pm get-app-links.
Do I need to verify each subdomain separately?
Yes. Every host you claim in the manifest must have its own assetlinks.json served at that host's /.well-known/ location. A subdomain does not inherit from the apex domain automatically.
Do Android App Links work on older Android versions?
The verification behavior (skipping the chooser) requires Android 6.0 (API level 23) or higher. On older versions, an HTTPS intent filter without verification still works but shows the chooser. In practice, you can ship App Links as your primary flow and treat older versions as showing the chooser as a graceful fallback.
What if the user does not have my app installed?
The URL opens as a normal HTTPS link in the user's default browser. There is no error, no broken state. If the URL points to a real page on your website, they see the web version. If you also want to route them to the Play Store and resume the original destination after install, you need deferred deep linking layered on top of App Links.
How long does verification take after install?
Verification is asynchronous and typically completes within about 20 seconds of install on a good connection. Plan for it to take longer on slow networks. Do not test App Links immediately after a fresh install without waiting.
Do Android App Links work in every in-app browser?
Not always. Some in-app browsers (notably Instagram, Facebook, and TikTok on certain OS versions) keep taps inside their webview and prevent the App Link from firing. The workaround is to route the tap through a service that detects the in-app browser and forces the URL into the system browser, where the App Link handoff can happen. Our guide on opening links in the app instead of the browser covers the fix.
The Takeaway
Android App Links are a small idea wrapped in a lot of infrastructure. One HTTPS URL, verified against your domain through Digital Asset Links, opens your Android app when installed and the browser when not. The mechanism is elegant, but the setup is unforgiving. Get the content type right, skip the redirects, match the SHA-256 fingerprint exactly, remember autoVerify="true", and lean on adb to see what Android actually thinks. Miss any of those and Android quietly falls back to the browser without telling you why.
If you would rather ship faster and skip the verification maintenance entirely, sign up for U2L AI and generate links that behave as App Links on Android, Universal Links on iOS, and web fallbacks everywhere else with a single paste. It is the difference between debugging Digital Asset Links for a week and shipping a working link in five seconds.
Sources: