# Deep Links for Transactional Email

Password resets, magic links and order confirmations that open your app instead of a browser tab — with one link record and no API call at send time.

## Why transactional email

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, while the app sits 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.

## 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 a link record per email, per user, or per token.

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.

## 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, which 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 the 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 | No | Yes |
| Click recorded | Yes | No |
| API call at send time | None | One per message |

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

1. **Set up app linking for your project.** 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.
2. **Create one link per message type.** 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.
3. **Swap the URL in your email template.** Replace your existing web URL with the LinkSense one and keep your token in the query string.
4. **Handle the URL in your app.** Read the incoming URL and route on the path and query.

```typescript
const token = await createResetToken(user.id);

await mailer.send({
  to: user.email,
  subject: 'Reset your password',
  html: renderTemplate({
    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 configurable 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 most common reason deep links fail in email. See [Deep Links in Email](/docs/deep-links-in-email).
