Glossary/Android platform
Chrome intent
Definition
A Chrome intent is a URL using the `intent://` scheme, which encodes an Android Intent — its target package, action, extras and a fallback URL — inside a link so that a web page can launch a specific app and specify what should happen when that app is absent.
This is the mechanism that predates App Links and still outlives them in practice, because it does the one thing a verified https:// link cannot: it names the package it wants and carries an explicit instruction for the not-installed case. It is also the single most misapplied piece of Android deep linking, because it works flawlessly in the browser it is named after and does nothing at all in several places teams routinely ship it.
The syntax, and the anchor it has to live in
<!-- Chrome only launches an intent from a user gesture.
A redirect on page load, a meta refresh, or a JS
navigation without a click will be blocked silently. -->
<a href="intent://product/42
#Intent
;scheme=myapp
;package=com.example.app
;S.utm_source=newsletter
;S.click_id=abc123
;S.browser_fallback_url=https%3A%2F%2Fexample.com%2Fproduct%2F42
;end">Open in the app</a>
<!-- Unwrapped, with no whitespace — this is the real value: -->
<!-- intent://product/42#Intent;scheme=myapp;package=com.example.app;S.browser_fallback_url=https%3A%2F%2Fexample.com%2Fproduct%2F42;end -->Everything before #Intent is the data URI the app receives, with scheme= supplying the scheme that the intent:// placeholder stands in for — so the app above is handed myapp://product/42. Everything after it is the Intent itself, as semicolon-separated key-value pairs terminated by ;end. Android parses this with Intent.parseUri(), which is why the grammar is a framework detail rather than a Chrome invention.
| Key | Meaning | Example |
|---|---|---|
scheme | Scheme substituted into the data URI | scheme=myapp |
package | Restrict resolution to one package | package=com.example.app |
action | Intent action; defaults to VIEW | action=android.intent.action.VIEW |
category | Added category, repeatable | category=android.intent.category.DEFAULT |
launchFlags | Intent flags, hex | launchFlags=0x10000000 |
S.<name> | String extra | S.click_id=abc123 |
i.<name> | Int extra | i.product_id=42 |
B.<name> | Boolean extra | B.is_promo=true |
l. f. d. s. c. b. | Long, float, double, short, char, byte | l.ts=1757548800 |
S.browser_fallback_url | Chrome-specific. Where to go if nothing handles the intent | URL-encoded https://… |
browser_fallback_url must be percent-encoded
It is a string extra inside a semicolon-delimited list, so an unencoded https://example.com/p?id=42 breaks the parse at the first ; or is truncated at the ?. Encode the whole value — https%3A%2F%2Fexample.com%2Fp%3Fid%3D42 — and note that query parameters inside it need a second round of encoding if they themselves contain a URL.
What Chrome strips, and what happens with no app
Chrome does not hand your fragment to Android verbatim. Before launching, it sanitises the parsed Intent: it adds the BROWSABLE category, and it discards the component and selector fields. That is deliberate — without it, any web page could target a non-exported activity inside any installed app by naming its class directly. The practical rule is that package= is honoured and component= is a waste of bytes.
| Situation | What Chrome does |
|---|---|
| App installed, filter matches | Launches the app with the data URI and extras |
Not installed, browser_fallback_url present | Navigates to the fallback URL |
Not installed, no fallback, package= set | Opens the Play Store listing for that package |
Not installed, no fallback, no package= | Nothing visible happens |
| No user gesture | Blocked, regardless of everything above |
Fallback is intent:// or javascript: | Rejected — the fallback must be a web URL |
The fourth row is the failure teams report as "the link does nothing". Chrome has correctly determined that no activity can handle the intent, found no instruction for that case, and stopped. Always set a fallback, and prefer a real web page over a store listing so that desktop, iOS and every unsupported browser land somewhere useful — the same reasoning as a link fallback chain.
<!-- The BROWSABLE category is not optional. Chrome adds it to the
intent, so a filter without it will not match. android:exported
must be true, and is required explicitly from Android 12. -->
<activity android:name=".DeepLinkActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" android:host="product" />
</intent-filter>
</activity>Note that the intent above targets a URI scheme, not a verified domain, so the filter is a scheme filter and no assetlinks.json is involved. That is a feature when you need a link to reach a package you name, and a liability in every other respect — see intent filters for why scheme-only filters are hijackable.
Where it works, and what to use instead
| Surface | intent:// | Verified App Link | Custom scheme |
|---|---|---|---|
| Chrome for Android | Yes | Yes | Yes |
| Samsung Internet, Edge, other Chromium | Usually | Yes | Yes |
| Firefox for Android | Partial | Yes | Yes |
| Android WebView in someone else's app | Only if they implement it | Only if they implement it | Only if they implement it |
| Instagram, Facebook, TikTok in-app browsers | Unreliable | Unreliable | Unreliable |
| Any browser on iOS | No | N/A | N/A |
| Desktop | No | N/A | N/A |
A raw WebView does not handle intent:// at all unless the hosting app overrides shouldOverrideUrlLoading, parses the URL and starts the activity itself. Since the hosting app is usually not yours, treat every embedded browser as a surface where the intent will fail — which is the same conclusion reached from the other direction in deep links in in-app browsers.
Verify the domain first, keep the intent as the escape hatch
A verified App Link is one https:// URL that works in every browser, every in-app WebView that honours the system handler, and as a shareable link that renders a preview. It cannot name a package or declare a fallback, which is precisely when intent:// earns its place: reaching a specific package deterministically, or handling a domain you cannot verify.
# The scheme the intent resolves to, sent straight to the package.
adb shell am start -W -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "myapp://product/42" com.example.app
# What actually resolves an intent URL on the device. Chrome's
# sanitisation is not reproduced here, so this is the upper bound
# of what will work, not a guarantee that Chrome will do it.
adb shell am start -a android.intent.action.VIEW -d "intent://product/42#Intent;scheme=myapp;package=com.example.app;end"
# Is the package's domain actually verified? If it is, drop the
# intent:// from the happy path and keep it only as a fallback.
adb shell pm get-app-links com.example.appDeep link debugger
Paste the intent URL or the https link behind it and the debugger resolves the whole chain — redirects, the fallback that would fire, and whether the domain is verified for App Links so you can tell if you still need the intent at all.
Open the deep link debugger →Frequently asked questions
- What is an intent:// URL?
- It is a URL that encodes an Android Intent inside a link, using the form intent://host/path#Intent;scheme=…;package=…;end. Android parses it with Intent.parseUri, so the fragment can carry an action, categories, typed extras and a target package, and Chrome adds a browser_fallback_url extra that says where to send the user when no installed app can handle the intent.
- Why does my intent:// link do nothing in Chrome?
- The two common causes are a missing user gesture and a missing fallback. Chrome will not launch an intent from a page-load redirect, a meta refresh or a scripted navigation — it needs a real click. If the gesture is present but the app is absent and you supplied neither browser_fallback_url nor a package to open in the Play Store, Chrome correctly determines there is nothing to do and stops silently.
- Does intent:// work on iOS?
- No. The intent:// scheme is an Android framework construct parsed by Intent.parseUri, so no browser on iOS can act on it, including Chrome for iOS, which uses the system web engine. iOS equivalents are Universal Links for a verified domain and a custom URI scheme for a named app, and neither supports declaring a fallback URL inside the link itself.
- Does Chrome honour the component field in an intent URL?
- No. Before launching, Chrome sanitises the parsed Intent by adding the BROWSABLE category and discarding the component and selector fields. This prevents a web page from targeting a non-exported activity inside an arbitrary installed app by naming its class. Use package= to restrict which app can respond; component= will be dropped.
- Should I use intent:// or a verified App Link?
- Prefer a verified App Link for anything shareable: one https URL that works across browsers, renders a link preview, and cannot be hijacked by another app claiming the same scheme. Keep intent:// for the two cases it uniquely covers — deterministically reaching a package you name, and declaring an explicit fallback URL for the not-installed case on a domain you cannot verify.
Related terms
- Android App Link — An Android App Link is an HTTPS URL that opens an Android app directly, without a chooser dialog, because the system has verified through a file on the domain that the app is authorised to handle it.
- Intent Filter — An intent filter is an element in an Android app's manifest that declares which intents an activity can handle, including the URL patterns that should open it.
- Custom URI Scheme — A custom URI scheme is a non-standard URL protocol, such as myapp://, that an app registers with the operating system so that URLs beginning with it open that app.
- Link Fallback Chain — A link fallback chain is the ordered set of destinations a single link resolves to — the app, the app store, or a web page — depending on the user's platform and whether the app is installed.
- Deep links in in-app browsers — An in-app browser is an embedded webview inside another app that renders links without handing them to the operating system, which prevents Universal Links and App Links from ever reaching the app that claims the domain.