Deep links for transactional email
Overview
Transactional email is where deep linking pays off most, because the recipient almost always already has your app. Someone asking for a password reset installed your app first. Sending them to a mobile browser to sign in, when the app is sitting on their home screen, is the exact failure deep linking exists to prevent.
It is also the cheapest place to apply it. A campaign link is created once by hand; a password-reset link is generated programmatically, forever. This guide shows the URL shape that makes that work with a single LinkSense link record.
When the app is installed
LinkSense claims every path on your project subdomain in the Apple App Site Association file, and the Android Digital Asset Links file has no path filter at all. iOS caches that association when your app is installed and matches it on-device.
So when a recipient taps https://acme.linksense.net/reset?token=abc123 on a phone with your app installed, the operating system opens your app and hands it the full URL — path and query string intact. LinkSense is never contacted. You do not need to create a link record per email, per user, or per token.
// iOS — the OS hands you the URL that was tapped, intact.func application( _ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL else { return false } // https://acme.linksense.net/reset?token=abc123 // Route it yourself — LinkSense does not store a screen for the link. router.handle(url) return true}LinkSense hands your app the URL. It does not store a destination screen or route for a link, so which view opens is decided by your own routing code — the same code you already use for any other URL.
Put the token in a query parameter
The URL shape only matters for recipients who do not have your app — a laptop, a new phone, someone who uninstalled. For them the link reaches LinkSense, and LinkSense has to find a matching link record.
It looks up the full path and query string first, then falls back to the path on its own. That fallback is what lets one record serve unlimited recipients — but only if the variable part of your URL is in the query string.
| Query parameter | Path segment | |
|---|---|---|
| Example | /reset?token=abc123 | /reset/abc123 |
| Link records needed | One, forever | One per token |
| Uninstalled visitor gets | Your store routing | A 404 |
| Token visible on the error page | — | Yes |
| Click recorded |
https://acme.linksense.net/reset?token=abc123 // One link record, custom path "reset".// Serves every recipient, forever. No API call at send time.https://acme.linksense.net/reset/abc123 // No link record matches. The visitor gets a 404 —// with the reset token rendered into the page.A path-segment URL misses the lookup, and the branded 404 renders the path — which means the reset token appears in the page. Keep single-use secrets in the query string.
Setting it up
Add your iOS bundle ID and Team ID, or your Android package name and signing fingerprint, and associate the domain in Xcode or your manifest. This is free, needs no SDK, and is what makes the link open your app at all.
A link at custom path reset, another at order, another at invite. Point each at the store or web destination an uninstalled visitor should get. You never create another one.
Replace your existing web URL with the LinkSense one and keep your token in the query string.
Read the incoming URL and route on the path and query, exactly as shown above.
// Your existing password-reset mail, unchanged except for the URL.const token = await createResetToken(user.id); await mailer.send({ to: user.email, subject: 'Reset your password', html: renderTemplate({ // Note the query parameter. One LinkSense link record at // custom path "reset" backs every message you ever send. resetUrl: `https://acme.linksense.net/reset?token=${token}`, }),});Two limits to know about
Query parameters are not forwarded to the app store. When an uninstalled visitor is routed onward, the store URL is used as configured; only the Google Play install referrer is appended. Your token survives to your app on the app-open path, not through a store install.
There is no wildcard or prefix matching. Link lookup is an exact match on the path, so /reset does not also match /reset/anything. Pattern routing is not something you can configure today.
Before you send: check your ESP
Most email providers rewrite every link in your message to their own click-tracking domain. That domain is not in the app-site-association file LinkSense serves, so the operating system has no app to open and the link falls back to a browser — even though everything above is configured correctly.
This is the single most common reason deep links fail in email, and it is worth checking before you ship.