{"id":541,"date":"2026-03-19T09:00:00","date_gmt":"2026-03-19T14:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=541"},"modified":"2026-03-07T03:32:43","modified_gmt":"2026-03-07T08:32:43","slug":"digital-asset-links-setup","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/digital-asset-links-setup\/","title":{"rendered":"Digital Asset Links: Setup and Verification Guide"},"content":{"rendered":"\n<p>Android App Links rely on a single JSON file hosted on your domain to prove that your app is authorized to handle URLs on that domain. That file is the Digital Asset Links file, hosted at <code>\/.well-known\/assetlinks.json<\/code>. Get it right, and Android routes matching URLs directly into your app. Get it wrong, and every matching link will show a disambiguation dialog instead.<\/p>\n\n\n\n<p>This guide covers the complete setup: what the JSON format looks like, how to extract your SHA-256 certificate fingerprint, how to host the file correctly, how to support multiple apps or multiple signing certificates, and how to confirm that Android accepted your configuration.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What Digital Asset Links Actually Does<\/h2>\n\n\n\n<p><a href=\"https:\/\/developers.google.com\/digital-asset-links\/v1\/getting-started\" rel=\"nofollow noopener\" target=\"_blank\">Digital Asset Links<\/a> is a Google protocol that lets a website declare which Android apps (and which keys those apps are signed with) are authorized to act as verified handlers for URLs on that site.<\/p>\n\n\n\n<p>When a user installs your app, Android reads the <code>android:autoVerify=&quot;true&quot;<\/code> attribute in your intent filters, identifies each domain your app claims to handle, and fetches <code>https:\/\/{domain}\/.well-known\/assetlinks.json<\/code> for each one. It checks whether your app&#39;s package name and SHA-256 certificate fingerprint appear in that file with the right <code>handle_all_urls<\/code> relation. If they do, verification passes and your app becomes the default handler for those URLs.<\/p>\n\n\n\n<p>For more context on how this fits into the full App Links picture, see the <a href=\"https:\/\/tolinku.com\/docs\/concepts\/app-links\/\">Tolinku App Links concepts documentation<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The assetlinks.json Format<\/h2>\n\n\n\n<p>The file is a JSON array. Each entry in the array is a &quot;statement&quot; that asserts a relationship between your site and a target. For App Links, the target is always an Android app.<\/p>\n\n\n\n<p>Here is the minimal format for a single app:<\/p>\n\n\n\n<pre><code class=\"language-json\">[\n  {\n    &quot;relation&quot;: [&quot;delegate_permission\/common.handle_all_urls&quot;],\n    &quot;target&quot;: {\n      &quot;namespace&quot;: &quot;android_app&quot;,\n      &quot;package_name&quot;: &quot;com.example.myapp&quot;,\n      &quot;sha256_cert_fingerprints&quot;: [\n        &quot;AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99&quot;\n      ]\n    }\n  }\n]\n<\/code><\/pre>\n\n\n\n<p>Key points about each field:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>relation<\/code>: Must be exactly <code>[&quot;delegate_permission\/common.handle_all_urls&quot;]<\/code>. This is the only relation type Android checks for App Links.<\/li>\n<li><code>namespace<\/code>: Always <code>&quot;android_app&quot;<\/code> for Android apps.<\/li>\n<li><code>package_name<\/code>: Your app&#39;s application ID, as declared in your <code>build.gradle<\/code> file. This is case-sensitive.<\/li>\n<li><code>sha256_cert_fingerprints<\/code>: An array of SHA-256 fingerprints of the certificates used to sign your app. Each fingerprint is 32 bytes represented as 64 hex characters separated by colons (95 characters total including colons).<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Getting Your SHA-256 Certificate Fingerprint<\/h2>\n\n\n\n<p>The fingerprint must match the certificate actually used to sign the APK or AAB that gets installed. If you use a different certificate for debug and release builds (which is standard practice), you need the fingerprint for each environment where you want verification to work.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">From a keystore file<\/h3>\n\n\n\n<p>If you manage your own keystore, use the <code>keytool<\/code> command that ships with the Java JDK:<\/p>\n\n\n\n<pre><code class=\"language-bash\">keytool -list -v -keystore \/path\/to\/your\/keystore.jks -alias your_key_alias\n<\/code><\/pre>\n\n\n\n<p>Enter your keystore password when prompted. The output will include a line like:<\/p>\n\n\n\n<pre><code>SHA256: AA:BB:CC:DD:EE:FF:...\n<\/code><\/pre>\n\n\n\n<p>That is the fingerprint you need.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">From a signed APK<\/h3>\n\n\n\n<p>If you have a built APK but not the keystore file directly available, extract the certificate with <code>apksigner<\/code>:<\/p>\n\n\n\n<pre><code class=\"language-bash\">apksigner verify --print-certs \/path\/to\/your.apk\n<\/code><\/pre>\n\n\n\n<p>Look for the <code>Signer #1 certificate SHA-256 digest<\/code> line in the output.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">From Google Play App Signing<\/h3>\n\n\n\n<p>If you use Google Play App Signing (which is now the default for new apps), Google re-signs your app with their own certificate. Your locally-generated signing key is no longer the key that gets installed on devices. In this case, you must get the fingerprint from the <a href=\"https:\/\/play.google.com\/console\" rel=\"nofollow noopener\" target=\"_blank\">Google Play Console<\/a>.<\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/doc-play-app-signing-diagram.png\" alt=\"Google Play App Signing flow diagram showing upload key and signing key separation\"><\/p>\n\n\n\n<p><em>Source: <a href=\"https:\/\/developer.android.com\/studio\/publish\/app-signing\" rel=\"nofollow noopener\" target=\"_blank\">Android Developer Documentation<\/a><\/em><\/p>\n\n\n\n<p>Navigate to: Setup &gt; App integrity &gt; App signing tab. You will find the SHA-256 certificate fingerprint listed under &quot;App signing key certificate.&quot; Copy that value directly.<\/p>\n\n\n\n<p>This is one of the most common reasons App Links verification fails: developers use their local keystore fingerprint but distribute through Play App Signing, so the installed certificate never matches.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Debug fingerprint for development<\/h3>\n\n\n\n<p>For testing on debug builds, get the debug keystore fingerprint:<\/p>\n\n\n\n<pre><code class=\"language-bash\">keytool -list -v -keystore ~\/.android\/debug.keystore -alias androiddebugkey -storepass android -keypass android\n<\/code><\/pre>\n\n\n\n<p>You can add both the debug and release fingerprints to the <code>sha256_cert_fingerprints<\/code> array so the same <code>assetlinks.json<\/code> file works for both.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Hosting the File Correctly<\/h2>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/platform-platform-domains.png\" alt=\"Tolinku custom domain configuration for deep links\"><\/p>\n\n\n\n<p>The file must be accessible at exactly this path:<\/p>\n\n\n\n<pre><code>https:\/\/{your-domain}\/.well-known\/assetlinks.json\n<\/code><\/pre>\n\n\n\n<p>Several hosting requirements are non-negotiable:<\/p>\n\n\n\n<p><strong>HTTPS only.<\/strong> Android will not fetch the file over plain HTTP. Your domain must have a valid TLS certificate.<\/p>\n\n\n\n<p><strong>No redirects.<\/strong> If <code>\/.well-known\/assetlinks.json<\/code> redirects to another URL (even another HTTPS URL), Android will not follow the redirect. The file must be served directly at that exact path.<\/p>\n\n\n\n<p><strong>Correct Content-Type.<\/strong> The response must include <code>Content-Type: application\/json<\/code>. Some static hosting providers serve unknown files with <code>text\/plain<\/code> or <code>application\/octet-stream<\/code>. Verify the response headers.<\/p>\n\n\n\n<p><strong>No authentication.<\/strong> The file must be publicly accessible without cookies, tokens, or any authentication headers.<\/p>\n\n\n\n<p><strong>Accessible from Android&#39;s servers.<\/strong> For Android 12 and later, Google&#39;s servers pre-fetch and cache the file rather than having each device fetch it at install time. This means the file needs to be reachable from Google&#39;s infrastructure, not just from within your own network.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Nginx configuration<\/h3>\n\n\n\n<p>If you are hosting with Nginx and need to set the correct Content-Type for the <code>.well-known<\/code> directory:<\/p>\n\n\n\n<pre><code class=\"language-nginx\">location \/.well-known\/assetlinks.json {\n    default_type application\/json;\n    add_header Cache-Control &quot;max-age=300&quot;;\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Apache configuration<\/h3>\n\n\n\n<p>For Apache, add a MIME type mapping in your <code>.htaccess<\/code> or server config:<\/p>\n\n\n\n<pre><code class=\"language-apache\">&lt;Files &quot;assetlinks.json&quot;&gt;\n    Header set Content-Type &quot;application\/json&quot;\n&lt;\/Files&gt;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Supporting Multiple Apps<\/h2>\n\n\n\n<p>If multiple apps should handle the same domain (for example, a main app and a beta or debug variant with a different package name), include a statement for each app in the array:<\/p>\n\n\n\n<pre><code class=\"language-json\">[\n  {\n    &quot;relation&quot;: [&quot;delegate_permission\/common.handle_all_urls&quot;],\n    &quot;target&quot;: {\n      &quot;namespace&quot;: &quot;android_app&quot;,\n      &quot;package_name&quot;: &quot;com.example.myapp&quot;,\n      &quot;sha256_cert_fingerprints&quot;: [\n        &quot;AA:BB:CC:DD:...&quot;\n      ]\n    }\n  },\n  {\n    &quot;relation&quot;: [&quot;delegate_permission\/common.handle_all_urls&quot;],\n    &quot;target&quot;: {\n      &quot;namespace&quot;: &quot;android_app&quot;,\n      &quot;package_name&quot;: &quot;com.example.myapp.beta&quot;,\n      &quot;sha256_cert_fingerprints&quot;: [\n        &quot;11:22:33:44:...&quot;\n      ]\n    }\n  }\n]\n<\/code><\/pre>\n\n\n\n<p>Each app entry is independent. A domain can authorize any number of apps simultaneously.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Supporting Multiple Signing Certificates<\/h2>\n\n\n\n<p>It is valid to list multiple fingerprints in <code>sha256_cert_fingerprints<\/code> for a single app. Android considers verification successful if the installed certificate matches any fingerprint in the array. This is useful when:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>You want to support both debug and release builds from the same domain.<\/li>\n<li>You are rotating signing keys and need both the old and new keys to work during the transition.<\/li>\n<li>Your CI pipeline uses a different signing certificate than your production releases.<\/li>\n<\/ul>\n\n\n\n<pre><code class=\"language-json\">&quot;sha256_cert_fingerprints&quot;: [\n  &quot;AA:BB:CC:DD:...&quot;,\n  &quot;11:22:33:44:...&quot;\n]\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Verifying the Setup<\/h2>\n\n\n\n<p>After hosting the file, verify it before testing on a device. The Google Digital Asset Links API provides a direct verification endpoint:<\/p>\n\n\n\n<pre><code>https:\/\/digitalassetlinks.googleapis.com\/v1\/statements:list?source.web.site=https:\/\/yourdomain.com&amp;relation=delegate_permission\/common.handle_all_urls\n<\/code><\/pre>\n\n\n\n<p>Open that URL in a browser (substituting your actual domain). If the file is valid and accessible, the API returns a response with <code>&quot;complete&quot;: true<\/code> and lists the statements it found. If the file has a problem, the response will include an error description.<\/p>\n\n\n\n<p>The <a href=\"https:\/\/developers.google.com\/digital-asset-links\/tools\/generator\" rel=\"nofollow noopener\" target=\"_blank\">Statement List tool<\/a> in the Google developer documentation also provides a form-based interface for checking your file.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Checking verification status on a device<\/h3>\n\n\n\n<p>After installing your app on a device, check whether Android accepted the verification:<\/p>\n\n\n\n<pre><code class=\"language-bash\">adb shell pm get-app-links --package com.example.myapp\n<\/code><\/pre>\n\n\n\n<p>The output shows each domain and its verification status. You want to see <code>verified<\/code> for each domain. If you see <code>none<\/code> or <code>rejected<\/code>, the file either has an error or the server configuration is blocking Android from fetching it.<\/p>\n\n\n\n<p>For a full walkthrough of testing commands and common verification failures, see <a href=\"https:\/\/tolinku.com\/blog\/testing-android-app-links\/\">Testing Android App Links<\/a> and the <a href=\"https:\/\/tolinku.com\/docs\/troubleshooting\/android\/\">Tolinku Android troubleshooting guide<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Common Mistakes<\/h2>\n\n\n\n<p><strong>Wrong package name.<\/strong> The <code>package_name<\/code> in the JSON must match the <code>applicationId<\/code> in your app&#39;s <code>build.gradle<\/code>, not the Java package name. These are often different in larger projects.<\/p>\n\n\n\n<p><strong>Missing the relation array format.<\/strong> The <code>relation<\/code> field must be an array, even though it contains only one string. <code>&quot;relation&quot;: &quot;delegate_permission\/common.handle_all_urls&quot;<\/code> (without brackets) is invalid JSON for this spec and will fail.<\/p>\n\n\n\n<p><strong>Stale cache during testing.<\/strong> Android and Google&#39;s servers cache the <code>assetlinks.json<\/code> file. After making changes, it can take several minutes to an hour for the new version to propagate. When debugging, use <code>adb<\/code> to trigger a manual re-verification rather than reinstalling repeatedly.<\/p>\n\n\n\n<p><strong>HTTP instead of HTTPS.<\/strong> Even if your site normally redirects HTTP to HTTPS, the <code>assetlinks.json<\/code> must be served at the HTTPS URL directly.<\/p>\n\n\n\n<p><strong>File served from a subdomain.<\/strong> If your app intent filter lists <code>example.com<\/code> as the host, the file must be at <code>https:\/\/example.com\/.well-known\/assetlinks.json<\/code>. A file at <code>https:\/\/www.example.com\/.well-known\/assetlinks.json<\/code> does not apply to <code>example.com<\/code>. You need separate files (or the same content) at each subdomain your app handles.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Using Tolinku for Managed App Links<\/h2>\n\n\n\n<p>If you are using Tolinku to manage your deep links, the platform handles the <code>assetlinks.json<\/code> file automatically. You configure your package name and certificate fingerprint in the Appspace settings, and Tolinku serves the correct file from your domain. See the <a href=\"https:\/\/tolinku.com\/docs\/user-guide\/configuring-android\/\">Tolinku Android configuration guide<\/a> for the setup steps.<\/p>\n\n\n\n<p>This removes the hosting complexity and ensures the file stays in sync as you add new apps or update signing certificates, without manual file editing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>The <code>assetlinks.json<\/code> file is small but precise. The format is straightforward, but the hosting requirements are strict: HTTPS, no redirects, correct Content-Type, and public access. The most common point of failure is a certificate fingerprint mismatch, usually because of Google Play App Signing. Get the fingerprint from the Play Console rather than your local keystore if you distribute through Google Play, add both debug and release fingerprints during development, and use the Digital Asset Links API to validate the file before testing on devices.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Digital Asset Links are the foundation of Android App Links verification. This guide walks through the JSON format, how to get your SHA-256 fingerprint, hosting at \/.well-known\/, and how to confirm verification passed.<\/p>\n","protected":false},"author":2,"featured_media":540,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Digital Asset Links: Setup and Verification Guide","rank_math_description":"Learn how to configure assetlinks.json for Android App Links. Covers JSON format, SHA-256 fingerprints, hosting, multiple apps, and verification.","rank_math_focus_keyword":"digital asset links","rank_math_canonical_url":"","rank_math_facebook_title":"","rank_math_facebook_description":"","rank_math_facebook_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-digital-asset-links-setup.png","rank_math_facebook_image_id":"","rank_math_twitter_title":"","rank_math_twitter_description":"","rank_math_twitter_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-digital-asset-links-setup.png","footnotes":""},"categories":[10],"tags":[25,23,20,34,30,33],"class_list":["post-541","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-android","tag-android","tag-app-links","tag-deep-linking","tag-kotlin","tag-sdks","tag-user-experience"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/541","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/comments?post=541"}],"version-history":[{"count":1,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/541\/revisions"}],"predecessor-version":[{"id":542,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/541\/revisions\/542"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/540"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=541"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=541"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=541"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}