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 links vs Universal/App Links
Section titled “Deferred links vs Universal/App Links”| Deferred links | Universal Links and App Links | |
|---|---|---|
| Purpose | Keep the link destination and campaign data when the app is not installed | Open the app directly, without loading a web page |
| When it applies | The app is not installed when the user clicks the link | The app is installed and the domain is verified on the device |
| What happens | Detour 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 |
| Matching | After 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
Section titled “Custom scheme links”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.
Register the scheme
Section titled “Register the scheme”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.
What Detour hosts
Section titled “What Detour hosts”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.
| File | Contents |
|---|---|
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.
Short link paths
Section titled “Short link paths”A short link is a single path segment on your Detour host. The format changed on 27 July 2026:
| Created | Path | Example |
|---|---|---|
| Since | the app hash, then the short link hash | /afcsvjRbhL051ksk |
| Before | the 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.
Dashboard configuration
Section titled “Dashboard configuration”Add this data to your App configuration so Detour can generate correct association files.
iOS configuration
Section titled “iOS configuration”- Bundle ID — for example
com.example.myapp. - Apple Team ID — from your Apple Developer account. It becomes the
TEAMID.bundleIdentries in the AASAappIDsarray.
Android configuration
Section titled “Android configuration”- Package name — for example
com.example.android. - SHA-256 signing certificate fingerprints — production, debug, or both. At least one is required. See Finding your Android SHA-256 fingerprint.
App-side setup
Section titled “App-side setup”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.
Verification
Section titled “Verification”Once Detour hosts the files, devices verify them automatically:
- iOS fetches the AASA file and verifies
appIDsandpaths. Once verified, Universal Links for matching paths open the app. - Android fetches
assetlinks.jsonand verifies thepackage_name+sha256_cert_fingerprints. If verified, intent filters withautoVerify="true"cause App Links to open the app.
Finding your Android SHA-256 fingerprint
Section titled “Finding your Android SHA-256 fingerprint”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 installed | Signed with |
|---|---|
| Google Play, any track | Play App Signing key |
| Side-loaded copy of the artifact you uploaded to Play | Upload key |
eas build, any profile, including development | Keystore in your EAS account |
eas build with "withoutCredentials": true | Project debug keystoreandroid/app/debug.keystore |
npx expo run:android or npx react-native run-android | Project debug keystoreandroid/app/debug.keystore |
| Flutter and native Android debug builds | Home debug keystore~/.android/debug.keystore |
| Release build signed with your own keystore | Your release keystore |
Read it from the keystore
Section titled “Read it from the 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:
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:9CFlutter and native Android debug builds use the debug keystore in your home directory:
keytool -list -v -keystore ~/.android/debug.keystore -storepass android | grep SHA256For your own release keystore, use its path. keytool asks for the keystore
password:
keytool -list -v -keystore <path-to-your.keystore> | grep SHA256Remove | 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:
cd android && ./gradlew signingReport # native Android projects: run in the project rootOne 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:signingReportVariant: debugConfig: debugStore: /Users/you/project/android/app/debug.keystoreAlias: androiddebugkeyMD5: 20:F4:61:48:B7:2D:8E:5E:5C:A2:3D:37:A4:F4:14:90SHA1: 5E:8F:16:06:2E:A3:CD:2C:4A:0D:54:78:76:BA:A6:F3:8C:AB:F6:25SHA-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:9CValid until: Wednesday, 1 May 2052----------Variant: releaseConfig: debugStore: /Users/you/project/android/app/debug.keystoreAlias: androiddebugkeyMD5: 20:F4:61:48:B7:2D:8E:5E:5C:A2:3D:37:A4:F4:14:90SHA1: 5E:8F:16:06:2E:A3:CD:2C:4A:0D:54:78:76:BA:A6:F3:8C:AB:F6:25SHA-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:9CValid until: Wednesday, 1 May 2052Here 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.
Read it from EAS
Section titled “Read it from EAS”Builds from eas build are signed with the keystore stored in your EAS account:
eas credentials -p androidSelect the build profile, then Keystore, and copy SHA256 Fingerprint.
Read it from the artifact
Section titled “Read it from the artifact”When you did not build the binary yourself, or want to be certain what a device is running, inspect the signature of the file:
apksigner verify --print-certs app.apk # build-tools; use $ANDROID_HOME/build-tools/*/apksigner if not on PATHkeytool -printcert -jarfile app.aab | grep SHA256 # .aab and other JAR-signed artifactsapksigner prints lowercase hex without separators, while assetlinks.json and the dashboard expect uppercase colon-separated:
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 App Signing key
Section titled “Play App Signing key”Play re-signs everything it delivers, internal testing included. The App signing page can be opened two ways:
- Protected with Play → Play Store protection card → Manage Play app signing.
- Test and release → App integrity → Go 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.
Verify
Section titled “Verify”curl -s https://your-org.godetour.link/.well-known/assetlinks.json # what Detour publishesadb shell pm verify-app-links --re-verify com.example.app # Android 12+adb shell pm get-app-links com.example.app # expect: verifiedHandling links in your app
Section titled “Handling links in your app”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 .