guides

The assetlinks.json file, explained

the linkboo team·6 min read·updated Mon Jun 01 2026 17:00:00 GMT-0700 (Pacific Daylight Time)
On this page

scope

The complete reference for the Android Digital Asset Links file (assetlinks.json). Format, fingerprint requirements, hosting, verification with adb and Google's API. This is the format spec — for the setup walkthrough, see /guides/android-app-links-setup. For the iOS equivalent, see /guides/aasa-file-explained.

Cluster C hub: /guides/firebase-dynamic-links-replacement.

the file

A JSON file served at:

https://yourdomain.com/.well-known/assetlinks.json

The file declares which Android apps are authorized to handle URLs on your domain. Android fetches and verifies it at app install time. If verification succeeds, App Links resolve directly to your app; if it fails, the system surfaces the "Open with" disambiguation dialog.

Hosting requirements:

Property Required value
Scheme https://
Path /.well-known/assetlinks.json
Content-Type application/json
Redirects None
TLS Publicly trusted certificate
Authentication None — public anonymous GET
File size Under a few KB typical; no hard cap documented
Subdomains Each subdomain requires its own assetlinks.json

the format

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.yourcompany.yourapp",
      "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"
      ]
    }
  }
]

The file is a JSON array of "statements." Each statement declares a relationship between a web property and a target. For App Links, the target is an Android app.

Fields:

Key Type Required Meaning
relation[] array of strings Yes Relationship URI. For App Links: delegate_permission/common.handle_all_urls
target.namespace string Yes android_app for App Links, web for other relations
target.package_name string Yes The app's applicationId (matches AndroidManifest package)
target.sha256_cert_fingerprints[] array of strings Yes One or more SHA-256 cert fingerprints

The sha256_cert_fingerprints array supports multiple fingerprints — required when you have separate debug + release builds, or both upload key + Play App Signing key.

getting the SHA-256 fingerprint

From a local keystore

keytool -list -v -keystore /path/to/keystore.jks -alias your-alias

Look for the SHA256: line. Format is 14:6D:E9:... — 64 hex characters separated by colons.

From the debug keystore (auto-created by Android Studio)

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

This is the fingerprint your Run button signs with. Required if you're testing App Links on a debug build.

From Google Play App Signing

When you opt into Play App Signing, Google re-signs your APK/AAB with a Play-managed key when distributing to users. The fingerprint Android verifies against is the Play-managed one, not your upload key.

In Play Console: → Your App → Setup → App Integrity → App Signing → "App signing key certificate" → copy SHA-256 certificate fingerprint.

Most production apps need BOTH fingerprints in assetlinks.json:

  • The upload-key fingerprint (used when sideloading or installing via Android Studio).
  • The Play-managed key fingerprint (used when installing from the Play Store).

Without the Play-managed fingerprint, verification fails for users who install from Play.

multiple apps, multiple domains

Multiple apps claiming the same domain:

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.yourcompany.app",
      "sha256_cert_fingerprints": ["<app fingerprint>"]
    }
  },
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.yourcompany.app.beta",
      "sha256_cert_fingerprints": ["<beta fingerprint>"]
    }
  }
]

Each app is a separate statement.

Multiple domains for a single app: each domain serves its own assetlinks.json. Subdomains do NOT inherit from parent domains. https://yourdomain.com/.well-known/assetlinks.json does not cover https://app.yourdomain.com/.

The web namespace declares a relationship between two web properties — most commonly, that one domain owns another. Not used for App Link verification, but appears in adjacent contexts (e.g., Google Sign-In domain ownership).

{
  "relation": ["delegate_permission/common.get_login_creds"],
  "target": {
    "namespace": "web",
    "site": "https://login.yourdomain.com"
  }
}

For Universal/App Link work, you'll only use android_app namespace statements. Mentioned here for completeness.

hosting

Nginx

location = /.well-known/assetlinks.json {
    default_type application/json;
    add_header X-Content-Type-Options nosniff;
    try_files /assetlinks.json =404;
}

Apache (.htaccess)

<Files "assetlinks.json">
    ForceType application/json
</Files>

Cloudflare Workers

addEventListener('fetch', event => {
  const url = new URL(event.request.url);
  if (url.pathname === '/.well-known/assetlinks.json') {
    event.respondWith(new Response(JSON.stringify(statements), {
      headers: { 'content-type': 'application/json; charset=utf-8' }
    }));
  }
});

Next.js / Vercel

Place at public/.well-known/assetlinks.json. The file extension is part of the URL on Android (unlike AASA, which is extensionless), so Vercel serves it correctly by default.

verifying the file

Google's Statement List API

The authoritative check — this is what Android uses internally.

curl -s "https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://yourdomain.com&relation=delegate_permission/common.handle_all_urls"

Expected response: a list of statements matching the file, with maxAge indicating Google's cache freshness.

If the response is empty or missing your statement, Android will not verify the App Link. Causes:

  • assetlinks.json not reachable from Google's crawler IPs.
  • Content-Type wrong.
  • File has malformed JSON.
  • Cloudflare or other CDN blocking the Google crawler User-Agent.

adb shell on a real device

After installing the app on a test device:

adb shell pm get-app-links com.yourcompany.app

Expected output:

com.yourcompany.app:
    ID: ...
    Signatures: [...]
    Domain verification state:
      yourdomain.com: verified

If the state is none (Android 10 and earlier) or 1024 (Android 11+, legacy unverified):

adb shell pm verify-app-links --re-verify com.yourcompany.app

This forces re-verification. For Android 12+ (API 31+), the user-preference state may need a reset:

adb shell pm set-app-links --package com.yourcompany.app 0 all
Symptom Cause Fix
pm get-app-links shows none after install File not reachable, or wrong fingerprint Check curl + verify fingerprint
Works on debug build, fails after Play Store release Play App Signing fingerprint missing Add Play-managed cert fingerprint
Works after sideload, fails after Play Store install Same as above Same as above
Verification fails sporadically Cloudflare bot challenge blocking Google crawler Allow Google crawler User-Agent through bot protection
Verification fails on Android 12+ specifically User-preference state set to 0 Reset with pm set-app-links
Some links open app, others don't intent-filter pathPrefix doesn't cover all URLs Check AndroidManifest declaration
Multiple apps on same domain, wrong one opens First-tap user picks, sticky thereafter Document the disambiguation UX

The Play App Signing fingerprint mismatch is the single most common production bug. Always include both upload-key and Play-managed fingerprints.

fingerprint extraction shortcuts

Inside Gradle build

You can extract the fingerprint at build time and verify it matches your assetlinks.json:

// build.gradle.kts (Module: app)
android {
    // ...
}

tasks.register("printSha256") {
    doLast {
        val keystore = file(System.getenv("KEYSTORE_PATH") ?: "${System.getProperty("user.home")}/.android/debug.keystore")
        exec {
            commandLine("keytool", "-list", "-v",
                       "-keystore", keystore.absolutePath,
                       "-alias", "androiddebugkey",
                       "-storepass", "android",
                       "-keypass", "android")
        }
    }
}

Run with ./gradlew printSha256.

From an installed APK

keytool -printcert -jarfile path/to/app.apk

Useful for checking what fingerprint a downloaded APK is signed with.

For non-app linking where assetlinks.json doesn't apply, the no-SDK linkboo path is at link.boo/api.

references

  • Google Digital Asset Links protocol specification
  • Android App Links Verification documentation
  • Play App Signing documentation
  • Google Statement List API reference

Stop losing the click after the tap.

linkboo escapes the in-app browser so your real page loads — fast.

Start for free →