Skip to content
Dashboard

Universal Links & App Links

When your app is already installed, an https Detour link can open it directly instead of loading a web page. iOS calls this a Universal Link, Android an App Link, and both require a signed association file on the link’s host.

Detour hosts those files for you. Supply the app identifiers in the dashboard, that is Bundle ID and Team ID for iOS, package name and signing fingerprint for Android, and Detour serves the matching payloads on your organization subdomain (for example your-org.godetour.link):

  • /.well-known/apple-app-site-association (iOS)
  • /.well-known/assetlinks.json (Android)

Both are generated per request host from the configuration you saved.

Deferred linksUniversal Links and App Links
PurposeKeep the link destination and campaign data when the app is not installedOpen the app directly, without loading a web page
When it appliesThe app is not installed when the user clicks the linkThe app is installed and the domain is verified on the device
What happensDetour records the click and redirects to the store. After install, the SDK asks Detour for the original link.The OS checks the domain association files and passes the URL to your app
MatchingAfter first open. Deterministic with the Play Install Referrer on Android, probabilistic otherwise.
flowchart TD
    A[User clicks an https link] --> B{App installed<br/>and domain verified?}
    B -->|Yes| C[Universal Link or App Link]
    C --> D[OS checks association files<br/>AASA or assetlinks.json]
    D --> E[App opens directly with the URL<br/>no browser]
    B -->|No| F[Deferred link]
    F --> G[Detour records the click<br/>and redirects to the store]
    G --> H[User installs and opens the app]
    H --> I[SDK asks Detour for the original link]
    I --> J{Matched?}
    J -->|Yes| K[App navigates to the intended content]
    J -->|No| L[App opens normally, organic install]

Custom scheme links are URLs that your app defines, such as myapp://promo/summer. The OS passes them to your app directly and does not check a domain association file. You can use them as a fallback where Universal Links and App Links are not available.

Custom schemes are optional. Detour can return the parsed link data, and your app handles the navigation.

A custom scheme link opens your app only after you register the scheme in the app. Registration differs between iOS and Android. Follow your SDK’s installation guide for the exact configuration.

Detour serves both association files under /.well-known/ on the host your app is assigned to. That is your organization subdomain, or your custom domain if you attached one. Each file is built from your dashboard configuration when it is requested.

FileContents
apple-app-site-association (iOS)Team ID, Bundle ID, the configured path pattern, and short link paths
assetlinks.json (Android)Package name and SHA-256 certificate fingerprints

If a host has no configuration, the request returns 404.

A short link is a single path segment on your Detour host. The format changed on 27 July 2026:

CreatedPathExample
Sincethe app hash, then the short link hash/afcsvjRbhL051ksk
Beforethe short link hash on its own/051ksk

Both forms still work, and the association files Detour serves cover both.

The prefix matters on Android, where App Link intent filters match a path prefix. A short link that starts with the app hash is already covered by the filter in your app’s manifest, so you can add short links without releasing a new version of the app. Older links do not start with the app hash and need a filter of their own, and the dashboard includes them in the Android snippet it generates for you.

Add this data to your App configuration so Detour can generate correct association files.

  • Bundle ID — for example com.example.myapp.
  • Apple Team ID — from your Apple Developer account. It becomes the TEAMID.bundleId entries in the AASA appIDs array.

The association files hosted by Detour are only one side of the setup. Your app must also register the generated host and paths with the operating system and deliver cold-start and runtime links to the SDK.

The exact files and callbacks depend on your framework. Follow the complete guide for your SDK:

The Dashboard generates native configuration for verified HTTPS links. Custom schemes are optional and must be registered separately in the app. Any change to entitlements, Expo app config, or AndroidManifest.xml requires a new native build.

Once Detour hosts the files, devices verify them automatically:

  • iOS fetches the AASA file and verifies appIDs and paths. Once verified, Universal Links for matching paths open the app.
  • Android fetches assetlinks.json and verifies the package_name + sha256_cert_fingerprints. If verified, intent filters with autoVerify="true" cause App Links to open the app.

The fingerprint saved in the dashboard must belong to the key that signed the APK installed on the device. Any other key fails verification and the link opens in the browser.

First find which key signed the build. Each key links to the section that shows how to read its fingerprint.

How the app is installedSigned with
Google Play, any trackPlay App Signing key
Side-loaded copy of the artifact you uploaded to PlayUpload key
eas build, any profile, including developmentKeystore in your EAS account
eas build with "withoutCredentials": trueProject debug keystore
android/app/debug.keystore
npx expo run:android or npx react-native run-androidProject debug keystore
android/app/debug.keystore
Flutter and native Android debug buildsHome debug keystore
~/.android/debug.keystore
Release build signed with your own keystoreYour release keystore

When you know which file signed the build, keytool returns the value directly, already uppercase and colon-separated, which is the format the dashboard expects:

Terminal window
keytool -list -v -keystore android/app/debug.keystore -storepass android | grep SHA256
SHA256: 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

Flutter and native Android debug builds use the debug keystore in your home directory:

Terminal window
keytool -list -v -keystore ~/.android/debug.keystore -storepass android | grep SHA256

For your own release keystore, use its path. keytool asks for the keystore password:

Terminal window
keytool -list -v -keystore <path-to-your.keystore> | grep SHA256

Remove | grep SHA256 to see the full certificate. A keystore holding several keys prints one block per alias. Pick the one whose Alias name matches the keyAlias in your signing config.

If you are unsure which keystore a variant uses, let Gradle resolve it:

Terminal window
cd android && ./gradlew signingReport # native Android projects: run in the project root

One block per variant: match Variant: to the build you install, then read SHA-256:, which is hyphenated here, unlike keytool. Store: names the file Gradle resolved.

Sample signingReport output
> Task :app:signingReport
Variant: debug
Config: debug
Store: /Users/you/project/android/app/debug.keystore
Alias: androiddebugkey
MD5: 20:F4:61:48:B7:2D:8E:5E:5C:A2:3D:37:A4:F4:14:90
SHA1: 5E:8F:16:06:2E:A3:CD:2C:4A:0D:54:78:76:BA:A6:F3:8C:AB:F6:25
SHA-256: 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
Valid until: Wednesday, 1 May 2052
----------
Variant: release
Config: debug
Store: /Users/you/project/android/app/debug.keystore
Alias: androiddebugkey
MD5: 20:F4:61:48:B7:2D:8E:5E:5C:A2:3D:37:A4:F4:14:90
SHA1: 5E:8F:16:06:2E:A3:CD:2C:4A:0D:54:78:76:BA:A6:F3:8C:AB:F6:25
SHA-256: 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
Valid until: Wednesday, 1 May 2052

Here release reports Config: debug, the template default described above.

signingReport reads local Gradle config only, so it cannot show the keystore EAS holds or the Play App Signing key.

Builds from eas build are signed with the keystore stored in your EAS account:

Terminal window
eas credentials -p android

Select the build profile, then Keystore, and copy SHA256 Fingerprint.

When you did not build the binary yourself, or want to be certain what a device is running, inspect the signature of the file:

Terminal window
apksigner verify --print-certs app.apk # build-tools; use $ANDROID_HOME/build-tools/*/apksigner if not on PATH
keytool -printcert -jarfile app.aab | grep SHA256 # .aab and other JAR-signed artifacts

apksigner prints lowercase hex without separators, while assetlinks.json and the dashboard expect uppercase colon-separated:

Terminal window
apksigner verify --print-certs app.apk \
| awk -F'digest: ' '/SHA-256 digest/ { print $2 }' \
| tr -d ' ' | tr 'a-f' 'A-F' | sed 's/../&:/g; s/:$//'

For an app already on a device: adb shell pm path com.example.app, then adb pull <returned path>.

Play re-signs everything it delivers, internal testing included. The App signing page can be opened two ways:

  • Protected with PlayPlay Store protection card → Manage Play app signing.
  • Test and releaseApp integrityGo to Protected with Play → then as above.

Copy the SHA-256 certificate fingerprint under App signing key certificate. The Upload key certificate on the same page is a different key. It signs what you send to Google, so use it only for artifacts you side-load yourself.

Terminal window
curl -s https://your-org.godetour.link/.well-known/assetlinks.json # what Detour publishes
adb shell pm verify-app-links --re-verify com.example.app # Android 12+
adb shell pm get-app-links com.example.app # expect: verified

You can handle Universal/App Links and custom scheme links yourself with your preferred navigation and linking library. Detour also resolves them for you, returning a route the same way it does for deferred links. For details, see the SDK Usage page .