Short answer: assetlinks.json is the Digital Asset Links file that lets Android verify your app owns a website's links. It lives at https://<host>/.well-known/assetlinks.json, must be a JSON array served as application/json with a 200 and no redirect, and names your package and the SHA-256 fingerprint of the key that signs the app users install. Test yours with curl -I on the file URL and adb shell pm get-app-links.
What is assetlinks.json?
assetlinks.json: a JSON statement list hosted on your domain that declares which Android apps, identified by package name and signing certificate fingerprint, may handle that domain's links as verified App Links.
Android checks the file when the app is installed, for every host in intent filters marked android:autoVerify="true". If the statement matches the installed app, links to that host open the app directly instead of the browser.
How is the file structured?
The file is an array of statements. Each statement grants a relation to a target app.
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example",
"sha256_cert_fingerprints": [
"14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
]
}
}]
relation: an array that must includedelegate_permission/common.handle_all_urlsfor App Links.target.namespace:android_appfor an app.target.package_name: the application ID frombuild.gradle.target.sha256_cert_fingerprints: one or more SHA-256 fingerprints of the signing certificate.
Google says "a website can declare associations with multiple apps within the same assetlinks.json file", so add one statement per app. The fingerprint field "supports multiple fingerprints", such as debug and production builds.
What makes a fingerprint valid?
SHA-256 certificate fingerprint: a hash of the app's signing certificate, written as colon-separated uppercase hex.
SHA-256 is 32 bytes, so a valid value is 32 uppercase hex pairs joined by 31 colons, 95 characters in total. Google's Digital Asset Links API rejects malformed values with a message that it "should be a colon-separated uppercase hex" string.
Common format errors:
- Lowercase hex.
- A 20-pair SHA-1 value instead of SHA-256.
- Missing colons or extra spaces.
For a local keystore, keytool -list -v -keystore my-release-key.keystore prints the fingerprint.
Why does Play App Signing change the fingerprint?
With Play App Signing, two keys are involved. Google's help describes them:
- Upload key: "You use this key to sign your app bundle before uploading."
- App signing key: "Google uses this key to sign the final APKs delivered to users' devices."
Google warns that "the certificate fingerprint produced by running keytool locally will usually not match the one on users' devices." Copy the app signing key fingerprint from Play Console under Protected with Play > Play Store distribution > Play app signing. Google says that page also shows a ready-made Digital Asset Links snippet. This is the cause behind App Links failing on Play installs.
What does the intent filter need?
The file is only half of App Links. The manifest needs a filter like this, with autoVerify:
<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="http" />
<data android:scheme="https" />
<data android:host="example.com" />
</intent-filter>
Google says "The android:autoVerify="true" attribute is required for App Links." On Android 12 and later, a web intent only opens your app if it is approved for that domain; otherwise it "resolves to the user's default browser app instead". See autoVerify missing.
Where do you host the file?
- At
https://<host>/.well-known/assetlinks.json. Google says statement lists elsewhere "are not valid for this site". - Over HTTPS, even if your filters also declare
http. - With
Content-Type: application/json. - With no 301 or 302 redirects. Any status other than 200 results in an empty statement list.
- On every host in the manifest. For a wildcard host such as
*.example.com, publish it at the root hostname.
On Android 11 and lower, one failing host stops all hosts verifying. Android 12 and later verify each host separately.
How do you verify with adb?
On Android 12 and later, install the app, wait at least 20 seconds, then:
adb shell pm set-app-links --package com.example.app 0 all
adb shell pm verify-app-links --re-verify com.example.app
adb shell pm get-app-links com.example.app
Wait a few minutes before reading results. Only verified means success; other states include none, approved, denied, legacy_failure and verifier error codes of 1024 and above. Test a link with adb shell am start -W -a android.intent.action.VIEW -d "https://example.com/product/1" com.example.app.
Google's other tools are the Statement List tester, the Digital Asset Links API (statements:list), the Play Console Deep links page under Grow, and Android Studio's App Links Assistant. The Android App Links setup guide covers the full flow.