Handle deep links and custom actions from push notifications, in-app messages, and embedded messages.
Prerequisites: SDK must be initialized (see integration-guide.md).
Deep links connect Iterable campaigns (push, in-app, embedded) to specific screens in the app. The marketing team sets link URLs in Iterable templates. The developer's role is to:
- Define how URLs map to app screens
- Register the URL handler with the SDK
- Configure which URL schemes the SDK is allowed to open
Custom actions (action:// URLs) are an alternative to deep links — they trigger app-specific logic (dismiss UI, toggle features, fire analytics) without navigating to a URL.
| Decision | Tier | Default | Notes |
|---|---|---|---|
| Does the app use a custom URI scheme? | 🟠 Important | No (HTTPS only) | e.g., myapp://. Must be registered with setAllowedProtocols. Ask the developer. |
| App Links (HTTPS verified) vs custom scheme | 🟡 Relevant | Custom scheme (simpler) | App Links require hosting assetlinks.json on the domain. Better for shared web/app URLs. |
| URL-to-screen mapping | 🔴 Critical | N/A | How do URLs map to app screens? The developer must define this — it's app-specific. |
Implement IterableCustomActionHandler? |
🟡 Relevant | No | Only needed if Iterable campaigns use action:// URLs. Ask the developer if marketing uses custom actions. |
| Behavior based on action source (push vs in-app vs embedded) | 🟢 Optional | Same for all sources | IterableActionContext.source tells you where the link came from. Most apps don't differentiate. |
Agent note: Ask the developer (🔴 Critical) what URL patterns the app uses and how they map to screens. Adapt the routing logic below.
class AppUrlHandler(private val appContext: Context) : IterableUrlHandler {
override fun handleIterableURL(uri: Uri, actionContext: IterableActionContext): Boolean {
if (uri.scheme == "yourscheme" && uri.host == "detail") {
val itemId = uri.pathSegments.firstOrNull()?.toIntOrNull() ?: return false
appContext.startActivity(Intent(appContext, DetailActivity::class.java).apply {
putExtra("item_id", itemId)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
})
return true
}
// Return false for URLs you don't handle — the SDK will try to open them with the system
return false
}
}Register in IterableConfig.Builder:
.setUrlHandler(AppUrlHandler(this))
.setAllowedProtocols(arrayOf("yourscheme")) // 🟠 Only if using custom schemesAgent note: Manifest intent filters are only needed if deep links arrive from OUTSIDE the Iterable SDK (e.g., from a browser, email, or other app). Links from Iterable push notifications, in-app messages, and embedded messages are routed through the
UrlHandlerregistered in Step 1 — they do NOT need manifest intent filters.Adding intent filters for the same scheme to multiple activities will cause Android to show a disambiguation dialog. Only add them if the developer specifically needs system-level deep link handling.
For the system to route URLs to the app (outside of the SDK handler), declare intent filters:
<activity android:name=".MainActivity" 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="yourscheme" />
</intent-filter>
</activity><activity android:name=".MainActivity" android:exported="true">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="yourdomain.com" />
</intent-filter>
</activity>Agent note: App Links require hosting a
/.well-known/assetlinks.jsonfile on the domain. This is usually a web team or DevOps task — inform the developer if they choose this path.
Only implement this if campaigns use action:// URLs. The SDK routes any non-openUrl action type here.
class AppCustomActionHandler : IterableCustomActionHandler {
override fun handleIterableCustomAction(action: IterableAction, actionContext: IterableActionContext): Boolean {
when (action.type) {
"dismiss" -> { /* dismiss current screen */ }
"showPromo" -> { /* navigate to promo screen */ }
else -> return false
}
return true
}
}Register in IterableConfig.Builder:
.setCustomActionHandler(AppCustomActionHandler())Agent note: Custom actions also apply to embedded message clicks. URLs starting with
action://oritbl://in embedded messages are routed through this handler.
- Custom schemes are blocked by default. The SDK only allows
httpsURLs. You MUST add.setAllowedProtocols(arrayOf("yourscheme"))or custom scheme links are silently dropped with a debug log. - Return
trueonly when you fully handled the URL. Returningtruetells the SDK to stop processing. If you returnfalse, the SDK tries to resolve the URL withACTION_VIEWintent (system/browser). handleIterableURLreceives links from all channels — push opens, in-app button taps, embedded message clicks. UseactionContext.sourceif you need to differentiate.- The SDK processes push open URLs through
IterableTrampolineActivity. This is an internal activity that handles the action and finishes. Don't interfere with it in your manifest. handleEmbeddedClickdoes NOT calltrackEmbeddedClickautomatically. If you build custom embedded UI, you must call bothhandleEmbeddedClick(for routing) andtrackEmbeddedClick(for analytics) yourself. The OOTB embedded views handle both.