Deep Links
Overview
Section titled “Overview”Trace delivers deep links to your app in two scenarios:
- Direct deep links — user clicks a Trace short link while the app is installed. The link opens the app directly.
- Deferred deep links — user clicks a link, installs the app, then opens it for the first time. Trace delivers the deep link payload after attribution.
Both arrive through the same listener.
Setting up the listener
Section titled “Setting up the listener”Trace.setDeepLinkListener { deepLink -> val path = deepLink.path // "/product/123" val params = deepLink.params // {"color": "blue"} val deferred = deepLink.isDeferred // true if from install attribution
navigateTo(path, params)}TraceClient.shared.setDeepLinkListener { deepLink in let path = deepLink.path let params = deepLink.params let deferred = deepLink.isDeferred
navigateTo(path, params)}Type-safe routing (Navigation3)
Section titled “Type-safe routing (Navigation3)”Dependencies
Section titled “Dependencies”Nav3 deep link support is split across two artifacts. Add both:
dependencies { implementation("io.traceclick:trace-sdk:<version>") // core SDK + mapper implementation("io.traceclick:trace-sdk-nav3:<version>") // Nav3 entry decorator}kotlin { sourceSets { commonMain.dependencies { implementation("io.traceclick:trace-sdk:<version>") // core SDK + mapper implementation("io.traceclick:trace-sdk-nav3-kmp:<version>") // Nav3 entry decorator (KMP) } }}The deep link mapper lives in the core SDK (com.trace.sdk.compose.rememberDeepLinkMapper). The Nav3-specific decorator and navigation helpers live in the Nav3 artifact (com.trace.sdk.nav3.rememberTraceEntryDecorator, com.trace.sdk.nav3.navigateReplacing).
Building a route mapper
Section titled “Building a route mapper”Use rememberDeepLinkMapper to convert deep link paths into typed route objects:
import com.trace.sdk.compose.rememberDeepLinkMapper
val mapper = rememberDeepLinkMapper { route("/product/{id}") { params -> ProductRoute(id = params.require("id")) } route("/invite/{code}") { params -> InviteRoute(code = params.require("code")) } route("/settings") { SettingsRoute }}Wiring into Navigation3
Section titled “Wiring into Navigation3”Pass the mapper to rememberTraceEntryDecorator from the Nav3 module:
import com.trace.sdk.nav3.rememberTraceEntryDecoratorimport com.trace.sdk.nav3.navigateReplacing
TraceProvider { val decorator = rememberTraceEntryDecorator( backStack = backStack, routeMapper = mapper )
NavDisplay( backStack = backStack, entryDecorators = listOf(decorator) )}When a deep link arrives with path /product/abc, the mapper calls your lambda and pushes ProductRoute(id = "abc") onto the back stack.
Action deep links
Section titled “Action deep links”Not every deep link maps to a screen. Use action() for deep links that trigger a side-effect without navigation, such as claiming a reward, toggling a feature flag, or applying a promo code:
val mapper = rememberDeepLinkMapper { // Navigation deep links route("/product/{id}") { params -> ProductRoute(id = params.require("id")) }
// Action deep links — no navigation, just a side-effect action("/claim/promo/{amount}") { params -> rewardRepository.claim(params.require("amount")) } action("/feature/{flag}/enable") { params -> featureFlags.enable(params.require("flag")) }}Under the hood the mapper returns a sealed DeepLinkResult:
sealed interface DeepLinkResult<out T> { data class Navigate<T>(val route: T) : DeepLinkResult<T> data class Action(val execute: () -> Unit) : DeepLinkResult<Nothing>}route() mappings produce Navigate results that are pushed onto the Nav3 back stack. action() mappings produce Action results whose execute block is invoked immediately by the decorator without any navigation change.
Path parameters
Section titled “Path parameters”Use {name} in the path pattern to capture segments:
route("/product/{id}") { params -> // params.require("id") -> "abc"}
route("/category/{cat}/item/{item}") { params -> // params.require("cat") -> "electronics" // params.require("item") -> "phone-123"}Query parameters
Section titled “Query parameters”Query parameters from deepLinkParams are also available:
// Link created with: deepLinkPath="/product/123", deepLinkParams={"color": "blue"}route("/product/{id}") { params -> val id = params.require("id") // "123" val color = params["color"] // "blue" ProductRoute(id, color)}Typed accessors
Section titled “Typed accessors”| Method | Return type | Behavior |
|---|---|---|
params["key"] | String? | Returns null if missing |
params.require("key") | String | Throws if missing |
params.int("key") | Int? | Parses to Int, null if missing or invalid |
params.long("key") | Long? | Parses to Long |
params.boolean("key") | Boolean? | Parses to Boolean |
Auth gates
Section titled “Auth gates”Deferred deep links often arrive before the user has signed in. Use an auth gate to hold the deep link until the user is authenticated:
import com.trace.sdk.nav3.rememberTraceEntryDecorator
val decorator = rememberTraceEntryDecorator( backStack = backStack, routeMapper = mapper, authGate = { isLoggedIn } // deep link waits until this returns true)The flow:
- User clicks campaign link, installs app, opens app
- SDK receives deferred deep link for
/invite/abc authGatereturnsfalse(user not logged in yet) — deep link is parked- User signs up or logs in —
authGatereturnstrue - Deep link is delivered —
InviteRoute(code = "abc")pushed to back stack
Creating links with deep link payloads
Section titled “Creating links with deep link payloads”When creating short links via the API, specify the deep link data:
curl -X POST https://api.traceclick.io/v1/links \ -H "X-Api-Key: tr_live_xxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "deepLinkPath": "/product/123", "deepLinkParams": {"color": "blue", "ref": "email"}, "campaignId": "summer_sale", "fallbackUrl": "https://yourapp.com/product/123" }'When this link drives an install, the SDK delivers:
path = "/product/123"params = {"color": "blue", "ref": "email"}isDeferred = true