Skip to content
CodeOnAir
← All posts

Android App Links not verified? Fix assetlinks.json

If your https links open Chrome instead of your app, Android couldn't verify your domain. Here's how to read pm get-app-links, fix assetlinks.json and the SHA-256 fingerprint, and re-verify.

Super Admin22 October 20268 min read

On Android, a verified App Link opens your app straight away when someone taps an https:// link. If verification fails, Android 12 and later open the link in the browser, and older versions may show a "which app?" chooser.

The good news: unlike iOS, Android tells you exactly which domains failed. This guide shows how to read that status, the five causes behind most failures, and how to make Android check again without reinstalling.

How App Link verification works

  1. Your app declares an intent filter with android:autoVerify="true" for one or more hosts.
  2. When the app is installed (or updated), Android fetches https://<host>/.well-known/assetlinks.json for every host in those filters.
  3. The file must list your package name and the SHA-256 fingerprint of the certificate that signed the installed APK.
  4. If it matches, the domain is marked verified and links to it open your app.

Since Android 12, each host is verified separately. On Android 11 and earlier, a single host that fails can stop all of the app's App Links from verifying, so one stray domain in your manifest can break everything.

Step 1: Read the verification state

Install the build you're testing, wait about 20 seconds, then run:

adb shell pm get-app-links com.example.shop

The output looks like this:

com.example.shop:
    ID: 01234567-89ab-cdef-0123-456789abcdef
    Signatures: [***]
    Domain verification state:
      links.example.com: verified
      www.example.com: legacy_failure
      example.org: 1026

What each state means (from Android's docs):

State Meaning
verified Works. Links to this host open the app.
none Nothing recorded yet. Wait a few minutes and re-verify.
legacy_failure The verifier rejected it; the reason isn't given. Usually the file or fingerprint.
approved / denied Forced by a shell command, not real verification.
1024 or higher A device-specific verifier error. Check the network and re-verify.

Any host that isn't verified won't open your app automatically.

Step 2: Check the assetlinks.json file

Fetch it the way the verifier does:

curl -v https://links.example.com/.well-known/assetlinks.json

It must:

  • Return HTTP 200 over HTTPS with a valid certificate.
  • Not redirect. Android's docs are explicit: a redirect such as http:// → https:// or example.com → www.example.com means verification fails.
  • Be served as application/json and be valid JSON.
  • Exist on every host in your intent filters. www.example.com and example.com are different hosts and each needs its own file.

A correct file:

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.shop",
      "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",
        "FA:C6:17:45:DC:09:03:78:6F:B9:ED:E6:2A:96:2B:39:9F:73:48:F0:BB:6F:89:9B:83:32:66:75:91:03:3B:9C"
      ]
    }
  }
]

(The fingerprints above are examples. Use your own.)

Then ask Google directly whether it can read your statements:

https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://links.example.com&relation=delegate_permission/common.handle_all_urls

If your package and fingerprint don't appear in the response, Google can't read your file. Fix the hosting before going further.

Step 3: Get the right SHA-256 fingerprint

Most real-world failures are here. The fingerprint must belong to the key that signed the APK on the device, and that differs depending on how the app was installed:

How the app was installed Signing key to list
From Google Play (production, internal testing, etc.) The Play App Signing key: Play Console → Test and release → App integrity → App signing
An APK or AAB you built and shared yourself Your upload / release key
./gradlew installDebug, Android Studio, react-native run-android, flutter run The debug key on your machine

With Play App Signing, Google re-signs your app, so the fingerprint of the keystore on your laptop won't match the build users install from Play. List every key you need. sha256_cert_fingerprints is an array, so the debug, upload and Play keys can all be listed together.

Get fingerprints locally:

# Debug key
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

# Your release / upload keystore
keytool -list -v -keystore path/to/release.jks -alias your-key-alias

# Every variant at once
./gradlew signingReport

Format matters: uppercase hex pairs separated by colons. Android's troubleshooting page specifically lists a lowercase fingerprint as a cause of failure.

Play Console also shows a ready-made Digital Asset Links JSON snippet on the App signing page. Copying it avoids typos.

Step 4: Check the intent filter

<activity
    android:name=".MainActivity"
    android:launchMode="singleTask"
    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" />
        <data android:host="links.example.com" />
    </intent-filter>
</activity>

Things to check:

  • android:autoVerify="true" is on the intent filter. Without it, nothing is verified.
  • All three of VIEW, DEFAULT and BROWSABLE are present.
  • Every host listed is one you control and that serves the file. A leftover host (an old Firebase page.link domain, a staging domain without a file) can stop all App Links verifying on Android 11 and earlier, and is simply unverified on Android 12+.
  • Keep custom schemes in a separate intent filter. Put myapp:// in its own filter without autoVerify, so the verified filter only contains the https hosts you mean to verify.
  • Manifest merging. In React Native, Flutter and multi-module apps, inspect the merged manifest (Android Studio → AndroidManifest.xml → Merged Manifest) to see what actually ships.

Step 5: Force Android to verify again

You don't need to reinstall after each fix. Reset and re-run verification:

adb shell pm set-app-links --package com.example.shop 0 all
adb shell pm verify-app-links --re-verify com.example.shop
# wait a minute or two, then:
adb shell pm get-app-links com.example.shop

Then test a link the way a user would open it:

adb shell am start -a android.intent.action.VIEW \
  -c android.intent.category.BROWSABLE \
  -d "https://links.example.com/product/42"

If the app opens directly with no chooser, verification works.

Two timing notes from Android's docs (checked 2026-10-22): on Android 15 and later, changes to assetlinks.json can take up to seven days to reach devices on their own, and on Android 14 and earlier they're only picked up when the app is installed or updated. During development, --re-verify is how you skip the wait.

Users can turn links off (and on)

Each app has a per-user setting: Settings → Apps → your app → Open by default. A user (or tester) may have turned off "Open supported links", or links may need adding by hand during development on some Android 13+ devices. Check here before assuming verification is broken.

The usual causes

  1. Play App Signing fingerprint missing. Works from Android Studio, fails from Play.
  2. Redirect on the file. Often a www redirect or a CDN rule.
  3. A host in the manifest without a file. Especially subdomains.
  4. The file isn't really JSON. A web framework returns its HTML 404 page with a 200 status, or the wrong content type.
  5. Lowercase or truncated fingerprint.

A quick checklist

Check Command or place
Which hosts fail adb shell pm get-app-links <package>
File is 200, JSON, no redirect, on every host curl -v https://host/.well-known/assetlinks.json
Google can read it Digital Asset Links API statements:list
Fingerprint of the key that signed this install Play Console App signing / ./gradlew signingReport
autoVerify="true", VIEW + DEFAULT + BROWSABLE Merged manifest
Re-verify after changes pm set-app-links … 0 all then pm verify-app-links --re-verify

Check it in one go

Our free Universal Link & App Link Validator checks assetlinks.json over HTTPS with redirects turned off, checks the content type and JSON structure, validates the package name and fingerprint format, and asks Google's Digital Asset Links API whether it can read your statements. It checks the iOS apple-app-site-association file too; for the iOS side, see Universal Links not working? An iOS debugging checklist.

With CodeOnAir AppLinks, you enter your package name and fingerprints (up to 20, so debug, upload and Play keys can all be listed) and CodeOnAir builds and serves assetlinks.json on every link domain for you. The dashboard's App setup box shows the intent filter with your domains filled in, and a Check button reads the files the way phones do. See the Android SDK setup and AppLink setup docs. Coming from Firebase? Remove your old page.link host from the manifest; the Firebase Dynamic Links alternative page covers the move. AppLinks is on the paid plans (pricing); sign-ups are currently by invitation, so request access.

FAQ

Why do my Android App Links open in the browser?

Android only opens your app automatically for hosts it has verified. If pm get-app-links shows anything other than verified for the host, the link goes to the browser (Android 12+) or shows a chooser. The usual reasons are a missing Play App Signing fingerprint, a redirect on assetlinks.json, or a host in the manifest with no file.

Where do I find the SHA-256 for Play App Signing?

Play Console → your app → Test and release → App integrity → App signing. Use the "App signing key certificate" SHA-256. Add your upload key and debug key as well if you install builds outside Play.

What does legacy_failure mean in pm get-app-links?

The verifier rejected the domain without giving a reason. Check the file over HTTPS without redirects, check the JSON and content type, and check that the fingerprint matches the key that signed the installed build. Then run pm verify-app-links --re-verify.

Does assetlinks.json need to be on the www and the root domain?

Yes, if both are in your intent filters. Every host is checked separately at its own /.well-known/assetlinks.json, and a redirect from one to the other fails verification.

How long does Android take to notice changes to assetlinks.json?

Per Android's docs (checked 2026-10-22), Android 14 and earlier only re-check on install or update; Android 15 and later can take up to seven days. During testing, adb shell pm verify-app-links --re-verify <package> checks again immediately.

Can one bad domain break all my App Links?

On Android 11 and earlier, yes: if any autoVerify host fails, none of the app's links are verified. On Android 12 and later, each host is verified separately.


Checked 2026-10-22 against Android Developers "Verify App Links" and "Troubleshoot App Links". Commands and states may change in future Android releases.

Your first AppLink in five minutes.

Create an app, pick a subdomain, and share a link that opens your app. Try Pro free for 30 days — no card needed.