Deep Linking
Direct, deferred, and contextual links — what each one carries, how the SDK delivers it after install, and when to reach for which.
A deep link points at a specific place inside your app rather than the home screen. On iOS the OS mechanism is Universal Links; on Android it's App Links. LinkTrail supports three flavors on top of them, and most apps use all three.
| Type | User has app? | What happens |
|---|---|---|
| Direct | Yes | OS opens the app straight to the path. |
| Deferred | No | User installs first; the path is delivered on first open. |
| Contextual | Either | Path plus custom data personalizes the first screen. |
The install gap
The browser can't hand parameters to a newly installed app — the stores sit in between and strip context. Without a deferred deep link, every install from a 'see this product' link lands on your home screen and the intent is lost. LinkTrail closes that gap: on first launch the SDK matches the install to the click and delivers the same link.
The link object
Every SDK delivers the same link shape to onLink (field casing varies slightly per language):
| Field | What it is |
|---|---|
| path | The in-app destination to route to — falls back to / when none is set. Route on this. |
| customData | String key/value payload configured on the link (voucher codes, referrer IDs, …). A nested object or array arrives as a compact JSON string — decode it yourself. |
| campaign / channel / utm | Campaign metadata for your own analytics |
| slug / url | The link's short slug and canonical URL |
| iosUrl / androidUrl / fallbackUrl | The routing destinations configured on the link |
| source (2nd argument) | deferred (first launch after install) or reengagement (app was installed) |
Deferred link delivery
Since 0.1.0 a deferred link is delivered at least once, on every platform. The SDK saves it on the device as soon as the install resolves it, and hands it to onLink once a handler is registered — in that launch or a later one. So a link survives a splash or login screen that registers onLink late, or an app killed before it got there.
- Make the handler safe to run twice. The link is marked routed only after the handler has been called, so a crash inside it can mean one redelivery on the next launch. Once it is marked routed, a relaunch never routes it again.
- It expires after 7 days. A saved link that no handler took within 7 days is dropped.
- The install is never re-sent to deliver it, and
resetForTestingclears the saved link. - Consent still decides when it resolves. While the decision is unset the install is held, so there is no link yet; either answer releases the install and the link routes. See Consent & privacy.
Which links the SDK claims
handleDeepLink returns true only for a LinkTrail link, which needs all three:
- 1The scheme is
https.httpis accepted only in debug builds (iOSDEBUG, a debuggable Android app). - 2The host is
linktrail.ioor a subdomain of it, or alinkDomainsentry or a subdomain of it. Case, the port and a trailing dot are ignored, and internationalised names are compared in punycode, so a look-alike host never matches. - 3The path has a slug —
https://yourapp.linktrail.io/alone is not a link.
A URL whose authority contains \, %, whitespace or a control character, has a bad port, or whose host isn't a valid host name is never claimed, so host-confusion URLs such as https://linktrail.io@evil.com/x are rejected. Anything else (mailto:, your own myapp://settings, an OAuth callback) returns false — the async forms throw notALinkTrailURL / NotALinkTrailUrl and React Native resolves null — with no request and no onError, and logs [LinkTrail] [DEBUG] Not a LinkTrail link — ignoring: <url>.
linkDomains entries are normalised at configure: scheme, path and port are removed, the host is lowercased and a trailing dot dropped, so https://Go.Acme.com:443/x becomes go.acme.com. An invalid entry (blank, a wildcard, an empty label, a space) is ignored with a WARNING; a single-label entry such as com is kept with a WARNING, because it matches every host under it.