Deep Linking
Implementing Android App Links with Digital Asset Links
How to verify domain ownership via the assetlinks.json file to ensure Android automatically opens your app instead of the browser.
In this article
Architecting the Digital Asset Links Protocol
The core of the verification process is a single file named assetlinks.json which must be hosted on your web server. This file acts as a public manifest that declares which mobile applications are authorized to handle links for your domain. It uses a simple JSON array structure to list package names and their corresponding certificate fingerprints.
When the Android system encounters an app with the autoVerify attribute set to true, it reaches out to the domain specified in the app manifest. It looks for this specific JSON file at a predefined location to confirm that the app's signature matches the one listed on the server. This two-way handshake ensures that only the developer who controls the domain can claim the traffic for that domain.
- The file must be served over a secure HTTPS connection to prevent man-in-the-middle attacks.
- The server must return a content-type of application/json for the verification request.
- The file must be accessible without any redirects and must not be blocked by robots.txt or authentication.
- The path to the file is strictly defined as /.well-known/assetlinks.json and cannot be changed.
Managing this file becomes more complex as your organization grows and you launch multiple versions of your app, such as staging, beta, and production builds. Each of these builds will likely have a different package name or be signed with a different certificate. You must ensure that the assetlinks file contains entries for every unique package and signature combination you wish to support.
Anatomy of the assetlinks.json File
The structure of the assetlinks file is intentionally minimal to reduce the chance of parsing errors by the Android system. Each entry in the array contains a relation field and a target field. The relation field typically specifies that the domain grants permission to open its links, while the target field identifies the app by its package name and SHA-256 certificate fingerprints.
1[
2 {
3 "relation": ["delegate_permission/common.handle_all_urls"],
4 "target": {
5 "namespace": "android_app",
6 "package_name": "com.production.fintech.app",
7 "sha256_cert_fingerprints": [
8 "EF:44:11:00:AA:BB:CC:DD:EE:FF:11:22:33:44:55:66:77:88:99:00:AA:BB:CC:DD:EE:FF:11:22:33:44:55:66"
9 ]
10 }
11 }
12]Note that the sha256_cert_fingerprints field is an array, allowing a single package to be associated with multiple signing keys. This is particularly useful when transitioning from a legacy signing key to a new one or when using Google Play App Signing. If you use Google Play App Signing, you must use the fingerprint provided in the Google Play Console rather than your local upload key.
Generating and Validating SHA-256 Fingerprints
Generating the correct fingerprint is the most common point of failure for developers implementing App Links. You can extract this fingerprint from your keystore file using the Java Keytool utility provided with the Android SDK. It is vital to use the SHA-256 version of the fingerprint, as MD5 or SHA-1 hashes will be ignored by the Android verification engine.
If your app is already live, you can also use the Digital Asset Links API to inspect the current state of your domain's verification. This allows you to verify that your server is correctly hosting the file and that the JSON syntax is valid before you release an update to your users. Always double-check that there are no hidden characters or formatting issues in the JSON string that could cause a parsing failure.
Configuring the Android Manifest for Auto-Verification
Once your server-side configuration is in place, you must instruct the Android operating system to attempt the verification process. This is done within the AndroidManifest.xml file by adding a specific attribute to your intent filters. The autoVerify attribute is a boolean flag that tells the system to validate the relationship between the app and the domains listed in the filter.
It is important to understand that the verification process is an all-or-nothing operation for each intent filter. If your intent filter contains multiple host entries, Android will attempt to verify every single one of them. If even one domain fails to host the assetlinks.json file correctly, the entire intent filter will fail verification, and the app will revert to showing the disambiguation dialog.
1<activity android:name=".ui.DeepLinkActivity">
2 <!-- The autoVerify attribute is crucial for App Links -->
3 <intent-filter android:autoVerify="true">
4 <action android:name="android.intent.action.VIEW" />
5 <category android:name="android.intent.category.DEFAULT" />
6 <category android:name="android.intent.category.BROWSABLE" />
7
8 <!-- Define the web domains to be handled -->
9 <data android:scheme="https" />
10 <data android:host="www.example-fintech.com" />
11 <data android:host="api.example-fintech.com" />
12 <data android:pathPrefix="/transfer" />
13 </intent-filter>
14</activity>By setting the scheme to https, you ensure that the app only intercepts secure links. While you can technically include the http scheme, Android's verification logic primarily focuses on secure connections. Modern best practices suggest moving all web traffic to HTTPS, which aligns perfectly with the security requirements of the Digital Asset Links protocol.
Handling Multiple Hostnames and Subdomains
Many large-scale applications operate across various subdomains, such as shop.example.com and blog.example.com. Each of these subdomains must be explicitly listed in the intent filter if you want the app to handle their specific paths. Remember that the Android system treats each unique host as a separate verification target that requires its own assetlinks.json file.
If you have a large number of subdomains, managing the verification files can become a logistical challenge. You should consider using a wildcard or a centralized redirection strategy on the web side, but ensure that the final destination of the .well-known path always returns the correct JSON. Consistency across all entry points is the key to maintaining a reliable deep linking experience for the user base.
Mapping URLs to Internal App Logic
Once the system verifies the link and opens your app, your activity must be prepared to parse the incoming URI and navigate to the correct screen. This usually involves extracting path segments or query parameters from the intent data. In the provided example, any URL starting with /transfer would be directed to the DeepLinkActivity, which would then extract the transaction ID.
A common pitfall is failing to handle scenarios where the app is already running in the background. You should ensure that your activity handles new intents properly by overriding the onNewIntent method. This prevents the system from creating multiple instances of the same activity and ensures a smooth transition within the app's internal navigation stack.
Deployment, Testing, and Troubleshooting
Verifying App Links can be a frustrating process because the verification happens silently in the background during app installation. There is no immediate visual feedback in the UI to tell you if the process succeeded or failed. Developers must rely on command-line tools and system logs to diagnose issues with the verification handshake.
The Android Debug Bridge provides a powerful set of commands to inspect the state of package verification on a connected device. By querying the package manager, you can see the status of every domain your app has attempted to verify. This is the only way to confirm that the system has successfully processed your assetlinks.json file and granted the app default handler status.
1# Check the verification status for a specific package
2adb shell dumpsys package d | grep -A 10 "com.production.fintech.app"
3
4# Force the system to run verification immediately
5adb shell pm set-app-links --state verified com.production.fintech.app allIf you see a status of ask or undefined, it means the verification failed or has not yet been attempted. Common causes include network timeouts, invalid JSON syntax, or a mismatch between the certificate fingerprint in the file and the one used to sign the APK. You should also check the logcat output during installation for any entries from the IntentFilterVerifier department.
The Impact of Android Versioning on Verification
It is important to note that the behavior of App Links has evolved significantly across different Android versions. Starting with Android 12, the system has become much stricter about verification requirements. If verification fails for a domain on Android 12 or higher, the system will never show the disambiguation dialog; it will simply open the link in the browser by default.
This change makes successful verification even more critical for maintaining a high-quality user experience. Developers targeting modern Android versions must ensure that their server infrastructure is robust enough to handle the verification requests. Relying on legacy deep linking behavior is no longer a viable strategy for apps that require seamless web-to-app transitions.
Common Pitfalls and Performance Considerations
A frequent mistake is hosting the assetlinks.json file on a server that requires a specific User-Agent or blocks automated requests. The Android system's verification agent may not identify itself like a standard browser, which can trigger security filters or firewalls. You should white-list the verification path to ensure it is globally accessible to all requests.
Performance is another factor to consider, as a slow server response can cause the verification process to time out. Since verification happens at install time, a slow response won't impact the user's immediate experience, but it will prevent the App Link from working until the next time the system attempts a check. Regularly monitor your server logs to ensure that the .well-known path is being served quickly and reliably.
