---
title: Kindly Android SDK — Deep Links & Link Interception Reference
slug: sdk/android-sdk-skill-deeplinks
description: Complete reference for deep links and link interception in the Kindly Android SDK. Use when handling kindly:// URLs from outside the app, opening specific dialogues from a deep link, or implementing KindlySDKInteraction to intercept links and button…
createdAt: 2026-11-04T11:39:56.407Z
---

Deep reference for two related but distinct features. Companion to [SKILL.md](https://docs.kindly.ai/sdk/android-sdk-skill.md).

| Feature               | Direction         | Mechanism                                                             |
| --------------------- | ----------------- | --------------------------------------------------------------------- |
| **Deep links**        | External → Kindly | `kindly://` URL scheme handled by `KindlySDK.handleUrl(context, uri)` |
| **Link interception** | Kindly → External | `KindlySDKInteraction.shouldHandleLink(url)`                          |

***

## Deep Links: `kindly://` URL scheme

The SDK accepts URIs of the form:

```javascript
kindly://chat                            → opens the chat UI
kindly://chat/dialogue/{dialogueId}      → opens chat and triggers the named dialogue
```

### 1. Register the intent filter

Add to your launcher (or a dedicated handler) `Activity` in `AndroidManifest.xml`:

```xml
<activity android:name=".MainActivity" android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

    <intent-filter android:autoVerify="false">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="kindly" android:host="chat" />
    </intent-filter>
</activity>
```

### 2. Hand incoming URIs to the SDK

### Activity (XML or Compose host)

```kotlin
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        intent?.data?.let { handleIfKindly(it) }
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        intent.data?.let { handleIfKindly(it) }
    }

    private fun handleIfKindly(uri: Uri) {
        if (KindlySDK.handleUrl(this, uri)) return
        // not a Kindly URL — handle elsewhere
    }
}
```

### 3. Programmatic dialogue trigger (no URL needed)

If the trigger is internal (a button in your own UI), skip the URL round-trip:

```kotlin
KindlySDK.triggerDialogue(id = "welcome_flow")
```

Requires an active connection (check `KindlySDK.state` first if unsure — see [EVENTS.md](https://docs.kindly.ai/sdk/android-sdk-skill-events.md)).

### `handleUrl(context:, uri:)` reference

```kotlin
public fun handleUrl(context: Context, uri: Uri): Boolean
```

| Returns | Meaning                                                                                                                  |
| ------- | ------------------------------------------------------------------------------------------------------------------------ |
| `true`  | The URI had scheme `kindly` and was processed (chat may have been opened, dialogue triggered, etc.).                     |
| `false` | The URI was not a Kindly URL **or** `start()` hasn't been called yet. Forward it to the rest of your app's URL handling. |

***

## Link Interception: `KindlySDKInteraction`

When a user taps a link **inside** the chat (in a bot message, button URL, etc.), Kindly normally opens it via an `ACTION_VIEW` intent. Implement `KindlySDKInteraction` to intercept links and button taps.

### Interface definition

```kotlin
package no.kindly.chatsdk.chat.common

interface KindlySDKInteraction {
    /** Called when a chat button is pressed. */
    fun didPressButton(chatButton: ButtonChat, chatLog: List<MessageChat>)

    /**
     * Called before opening a link from the chat.
     * Return true to let Kindly open it (default).
     * Return false to handle it yourself; Kindly will not open the link.
     */
    fun shouldHandleLink(url: String): Boolean = true
}
```

`shouldHandleLink` has a default implementation returning `true`, so you only need to override it when intercepting.

### Wiring it up

`interface` is a Kotlin keyword — escape with backticks when assigning:

```kotlin
class ChatLinkHandler : KindlySDKInteraction {
    override fun didPressButton(chatButton: ButtonChat, chatLog: List<MessageChat>) {
        Log.d("Kindly", "User tapped: ${chatButton.label}")
    }

    override fun shouldHandleLink(url: String): Boolean {
        if (url.startsWith("https://example.com")) {
            AppRouter.open(url)
            return false
        }
        return true
    }
}

KindlySDK.`interface` = ChatLinkHandler()
```

Hold a strong reference to the handler (e.g., as a property on your `Application` or singleton); the SDK does not retain it for you.

***

## Combined Pattern: deep links + interception

A common setup: external `kindly://` deep links open chat, *and* in-chat links route through your nav stack instead of a browser intent.

```kotlin
class KindlyIntegration(private val app: Application) : KindlySDKInteraction {

    fun boot() {
        KindlySDK.start(application = app, botKey = "YOUR_BOT_KEY", market = "YOUR_MARKET")
        KindlySDK.`interface` = this
    }

    fun handleExternal(activity: Activity, uri: Uri): Boolean {
        return KindlySDK.handleUrl(activity, uri)
    }

    override fun didPressButton(chatButton: ButtonChat, chatLog: List<MessageChat>) {
        Analytics.track("kindly_button_tap", chatButton.label)
    }

    override fun shouldHandleLink(url: String): Boolean {
        if (url.startsWith("https://example.com")) {
            AppRouter.open(url)
            return false
        }
        return true
    }
}
```

***

## Receiving Button-Click Details via Events

`KindlySDKInteraction.didPressButton` gives you the legacy `ButtonChat` shape. For richer context (button type, exchange info, full chat log) use the `kindly:buttonclick` event instead — see [EVENTS.md](https://docs.kindly.ai/sdk/android-sdk-skill-events.md):

```kotlin
lifecycleScope.launch {
    KindlySDK.events.collect { event ->
        val click = event.detail as? KindlyEventDetail.ButtonClick ?: return@collect
        Log.d("Kindly", "Tapped [${click.buttonType}] ${click.button.label}")
    }
}
```

Both paths fire for the same tap; pick whichever fits your existing wiring.

***

## Notes & Gotchas

- `handleUrl(...)` returns `false` if `start()` hasn't been called — the SDK can't open chat until it's initialized. Either start the SDK in `Application.onCreate` before forwarding URIs, or queue the URI and replay after `start()`.
- `kindly://chat/dialogue/{id}` requires the dialogue ID to exist in the bot configuration. Unknown IDs are logged as warnings; chat still opens but no dialogue is triggered.
- Set the intent filter on the **activity that handles deep links** (often your launcher activity). Without it, Android won't route `kindly://` intents to your app.
- Always assign `KindlySDK.`interface\`\` with backticks — `interface` is a reserved word in Kotlin.
- The `KindlySDKInteraction` is held by the SDK but is replaceable — assigning a new instance overwrites the previous one.
- Use `intent.data` in **both** `onCreate` (cold start) and `onNewIntent` (warm start with `singleTask`/`singleTop` launch modes) to catch all entry points.
