Operate
Testing and debugging
Check a link from the outside, on Android and on iOS.
A link that "opens the browser instead of the app" is almost always a verification problem (the hosted files, your app's configuration, or the device's cached state), not a redirect problem. Check each layer from the outside in. Use the test host for repeated checks: every request with a browser user agent is recorded as a click.
HOST=acme-test.becklinks.com # your link host
LINK_PATH=summer # the path, without the leading slash (case-sensitive)
APP_ID=ABCDE12345.com.acme.app # Apple Team ID.bundle ID
PKG=com.acme.app # Android package name1. The files the platforms read#
# iOS: 200, application/json, no redirect, your app ID in the list
curl -sS -o /dev/null -w '%{http_code} %{content_type} redirect=[%{redirect_url}]\n' \
"https://$HOST/.well-known/apple-app-site-association"
curl -sS "https://$HOST/.well-known/apple-app-site-association" | jq .
# Android: 200, application/json, your package and every signing fingerprint
curl -sS "https://$HOST/.well-known/assetlinks.json" | jq .If the content is wrong, fix the app settings in the dashboard (Team ID, bundle ID, package name, fingerprints); the files change at once. The Setup check and the domain diagnostics in the dashboard run these checks for you.
2. What Apple and Google see#
Their caches decide, not our server:
curl -sS "https://app-site-association.cdn-apple.com/a/v1/$HOST" | jq .
curl -sS "https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://$HOST&relation=delegate_permission/common.handle_all_urls" | jq .Apple's CDN refreshes on its own schedule (up to about a day) and devices read from it when the app is
installed or updated. For development builds use applinks:HOST?mode=developer in the entitlements and
turn on Settings, Developer, Associated Domains Development on the phone.
3. How a click is routed#
curl -sS -o /dev/null -w '%{http_code} -> %{redirect_url}\n' -A 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 Version/18.0 Mobile/15E148 Safari/604.1' "https://$HOST/$LINK_PATH"| Client | Expected |
|---|---|
| iPhone Safari | 302 to the App Store, or a landing page with Get the app when pasteboard matching is on |
| Android Chrome | 302 to Google Play with referrer=click_id… |
| Instagram or another in-app browser | A page with Open in app and Get the app |
| Desktop | 302 to your desktop or web URL, or a QR page |
| Link-preview bot | 200 HTML with Open Graph tags |
The dashboard Link Tester shows the same for a link of yours without the curl commands.
4. On an Android device#
adb shell pm get-app-links "$PKG" # state per host: verified, none, legacy_failure…
adb shell pm verify-app-links --re-verify "$PKG" # Android 12+; wait a few minutes
adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE \
-d "https://$HOST/$LINK_PATH" # verified: the app opens, no chooserverified is what you want. none means nothing is recorded yet (wait, then re-verify). Android 14
and older verify only at install or update, so reinstall after a fix. Check the fingerprint of the
installed app: adb shell pm path "$PKG", pull the APK and run apksigner verify --print-certs. It
must be in assetlinks.json (for Play installs, that is the Play App Signing key).
5. On an iPhone#
- The app's entitlements must contain
applinks:HOSTfor every host it should open, test host included:codesign -d --entitlements :- /path/to/App.app. - Tap the link in Notes or Messages. Typing it into Safari's address bar never opens an app. If a user once chose the browser, long-press the link and choose Open in App.
- Console on a Mac, filtered by
process:swcd, shows how the phone downloads and validates the association file when you reinstall the app.
6. Deferred links#
- Android: use a Play internal testing build;
adb installandflutter runhave no install referrer. - iOS: turn on pasteboard matching, pass
enablePasteboard: true, tap Get the app on the landing page, install through TestFlight or Xcode, open and allow the paste.
7. The SDK#
Run a debug build with configure(apiKey: …, logLevel: LogLevel.debug). getDiagnostics() returns
the environment, the install, the first-open result and why a link was not matched
(unmatchedReason), without any key or user id. debugOpenLink(uri) handles a URL as if the system had
opened the app with it. More in Troubleshooting.
Verify it works#
When steps 1 to 4 (Android) or 1, 2 and 5 (iOS) are clean and a link tapped in Notes or Messages opens the app, the setup is right. Do the same on the live host before release: Going live.