Skip to main content

Android SDK

Native Android SDK for deep linking, mobile attribution, and conversion tracking.

Faster setup with AI

Using Claude Code, Cursor or Codex? Copy this prompt and paste it into your agent from the root of your project. It installs the SDK, wires deep links through your navigation (asking you which screen each link should open), adds one event, and verifies the install in your LinkForty dashboard.

Install the LinkForty SDK in a native Android app
Kotlin SDK via Gradle, App Links, deferred and direct deep links (including onNewIntent), and a first in-app event.
Show the prompt
I use LinkForty (https://linkforty.com), a deep-linking and mobile attribution platform: short links that route each click to the app, the app store or the web, and record clicks, installs and in-app events per link.

Before writing any code, read the LinkForty documentation index at https://docs.linkforty.com/llms-full.txt and the pages listed under "Docs" below. Work from what the docs say, not from assumptions about how similar products work. If something you need is not in the docs or in this prompt — an ID, a token, a URL — stop and ask me for it rather than inventing a placeholder that looks real.

## Task: install the LinkForty Android SDK

Add the LinkForty SDK to this app so that an install that follows a click on one of my links is attributed to that link, a link's deep-link path opens the right screen on first launch (deferred deep linking) and on later opens (Universal Links / App Links), and one key in-app event is recorded.

## Values

- **App token**: shown in the LinkForty dashboard under Workspace Settings → App Token (it starts with `at_`). Ask me for it; put it in the app's config or environment, not hard-coded in a committed source file.
- **Base URL**: `https://api.linkforty.com` for LinkForty Cloud. (Only a self-hosted LinkForty server uses a different origin — ask if you are not sure which this is.)
- **Link host**: the host my links are on, shown in the dashboard under Settings → Domains — the workspace's own host (it looks like `something.toapp.me`) and any verified custom domain listed there. Ask me for it. This is the domain the app must claim for Universal Links / App Links.
- **LinkForty MCP server** (optional): if it is configured in this environment (https://mcp.linkforty.com/mcp, or the `linkforty-mcp` command), use `get_app_config` to read the workspace's app settings and `create_link` to make a test link at the end. Do not set it up for me if it is not.

## How to work

1. **Look before you change anything.** Identify the framework, its version, the navigation/router library and where the app initializes at launch. List the screens (routes) you found, and **ask me which screen each incoming link should open and what parameter it needs** — for example "a link with `productId` opens the product screen". Wait for my answer before editing; do not guess routes.
2. **Work on a new git branch.** If the working tree has uncommitted changes, stop and tell me instead of building on top of them.
3. **Follow the docs pages named below, not memory.** Package names, method names and config keys come from those pages; LinkForty's SDKs are not the same as other attribution SDKs you may know.
4. **Do not invent values.** Anything in "Values" that says "ask me" is something you ask for. Never commit a token to source.
5. **Keep the app working without a link.** A launch with no matching click must behave exactly as before: no crash, no error surfaced to the user, no blocked start-up.
6. **Finish with a summary**: every file you changed and why; the manual steps that remain for me (Apple Team ID and bundle ID, Play signing SHA-256 fingerprint, store listing, a rebuild and reinstall) and where in the LinkForty dashboard each one goes (Workspace Settings → App Configuration); and anything you were unsure about.
7. **Verify.** Run the app once, then check the LinkForty dashboard: the SDK Doctor badge in the sidebar (https://docs.linkforty.com/guides/attribution/sdk-health) should report the SDK as installed. If the MCP server is available, create a test link with `create_link` and open it on a device or simulator; the click and the deep link should reach the screen we agreed on.

## Steps

1. **Install** by adding `implementation("com.linkforty:sdk:<version on the docs page>")` to the app module's Gradle dependencies (https://docs.linkforty.com/sdks/android#installation).
2. **Initialize once** in the `Application` subclass's `onCreate` (create one if the app has none, and register it in the manifest): build a `LinkFortyConfig(baseURL = …, appToken = …, debug = BuildConfig.DEBUG)` with the base URL and app token from "Values" and call `LinkForty.initialize(this, config)` (https://docs.linkforty.com/sdks/android#initialize-the-sdk). Leave `apiKey` out — it is only for creating links from the app.
3. **Claim the link host** (https://docs.linkforty.com/sdks/android#app-links-setup): add an `<intent-filter android:autoVerify="true">` with `<data android:scheme="https" android:host="<link host>" />` to the main activity, one `<data>` element per host if there is also a custom domain, and set `android:launchMode="singleTask"` on that activity. LinkForty serves assetlinks.json on that host itself; nothing to upload. Tell me the package name and the signing certificate SHA-256 fingerprint (release and debug) must be entered in LinkForty under Workspace Settings → App Configuration, or the OS will not verify the domain.
4. **Deferred deep link**: register `LinkForty.shared.onDeferredDeepLink { data -> … }` right after initialize. When `data` is non-null, route to the screen we agreed on using its `deepLinkPath` / `customParameters`; when it is null (organic install) do nothing (https://docs.linkforty.com/sdks/android#handle-deferred-deep-links).
5. **Direct deep links** (https://docs.linkforty.com/sdks/android#handle-direct-deep-links): in the main activity pass `intent.data` to `LinkForty.shared.handleDeepLink(uri)` from both `onCreate` **and** `onNewIntent` — without `onNewIntent` a link tapped while the app is in the background is lost (https://docs.linkforty.com/sdks/android#important-onnewintent) — and register `LinkForty.shared.onDeepLink { url, data -> … }` to route the same way through the app's existing navigation.
6. **One in-app event**: at the obvious success moment in this app (sign-up completed, purchase, or the first key action — tell me which you chose) call `trackEvent` with `signup`, or `trackRevenue` with amount and ISO currency for a purchase (https://docs.linkforty.com/sdks/android#track-events). No personal data in properties. If the app uses ProGuard / R8, apply the keep rules on the docs page.

## Docs

- Android SDK: https://docs.linkforty.com/sdks/android (Installation, Initialize the SDK, Handle Deferred Deep Links, Handle Direct Deep Links, App Links Setup, Track Events)
- SDK integration overview: https://docs.linkforty.com/guides/attribution/sdk-integration
- Deferred deep linking: https://docs.linkforty.com/guides/attribution/deferred-deep-linking
- Link hosts and app association files: https://docs.linkforty.com/guides/links/custom-domains#your-linkforty-domain
- SDK Doctor: https://docs.linkforty.com/guides/attribution/sdk-health

## Done when

- Opening one of my links on a device without the app, installing it and launching it lands on the screen we agreed on, and the install appears under Installs in the LinkForty dashboard attributed to that link.
- Opening the same link with the app installed goes straight to that screen without a browser stop.
- The in-app event shows under Events, attached to that install.
- SDK Doctor in the dashboard sidebar reports the SDK as installed.

When you are done, tell me: exactly which files changed and why, how I can verify it worked end to end (what to tap or run, and what I should see in the LinkForty dashboard), and anything you were unsure about.

It needs your app token; the prompt asks you for it, and the MCP server can supply it if you have that set up.

Features​

  • Deferred deep linking -- route new users to specific content after install
  • Direct deep linking -- handle App Links and custom URL schemes
  • Install attribution -- probabilistic fingerprint matching
  • Event tracking -- log in-app events tied to attribution data
  • Revenue tracking -- structured purchase tracking with BigDecimal precision
  • Offline event queue -- events persist across app restarts and retry automatically
  • Programmatic link creation -- create short links from your app
  • Privacy-first -- no GAID collection

Requirements​

  • Android API 26+ (Android 8.0 Oreo)
  • Kotlin 1.9+
  • JDK 17

Installation​

Gradle (Kotlin DSL)​

dependencies {
implementation("com.linkforty:sdk:1.3.0")
}

Gradle (Groovy)​

dependencies {
implementation 'com.linkforty:sdk:1.3.0'
}

Quick Start​

Initialize the SDK​

Initialize once in your Application class or main Activity:

import com.linkforty.sdk.LinkForty
import com.linkforty.sdk.LinkFortyConfig

class MyApplication : Application() {
override fun onCreate() {
super.onCreate()

val config = LinkFortyConfig(
baseURL = "https://go.yourdomain.com",
apiKey = "your-api-key", // optional -- required for link creation
appToken = "at_your_app_token", // recommended for Cloud -- enables organic-install attribution
debug = true,
attributionWindowHours = 168 // 7 days (default)
)

LinkForty.initialize(this, config)
}
}
LinkForty.shared.onDeferredDeepLink { deepLinkData ->
if (deepLinkData != null) {
println("Attributed install: ${deepLinkData.shortCode}")
val productId = deepLinkData.customParameters?.get("productId")
if (productId != null) {
navigateToProduct(productId)
}
} else {
println("Organic install")
}
}

Pass incoming intents to the SDK from your Activity:

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
handleIntent(intent)
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleIntent(intent)
}

private fun handleIntent(intent: Intent) {
intent.data?.let { uri ->
LinkForty.shared.handleDeepLink(uri)
}
}
}

Register a callback to receive parsed data:

LinkForty.shared.onDeepLink { url, deepLinkData ->
println("Deep link opened: $url")
deepLinkData?.let { data ->
val route = data.customParameters?.get("route")
if (route != null) {
navigateToRoute(route)
}
}
}

Track Events​

// Custom event
LinkForty.shared.trackEvent(
name = "add_to_cart",
properties = mapOf("productId" to "123", "category" to "electronics")
)

// Revenue event with BigDecimal precision
LinkForty.shared.trackRevenue(
amount = BigDecimal("29.99"),
currency = "USD",
properties = mapOf("productId" to "123")
)

Every event is automatically credited to the deep link that most recently opened the app (last-click attribution), along with an app-open session — so analytics can show what users do after clicking a link. Events with no preceding deep-link open are reported as organic. No extra code is required.

Track Screen Views​

Reporting screen views lets analytics build a per-link screen-flow funnel. Each screen_view carries the same last-click attribution as your other events.

Automatic (Jetpack Navigation) — attach LinkFortyNavObserver to your NavController; each destination's route (Compose) or label (XML nav graph) is reported as it appears:

import com.linkforty.sdk.navigation.LinkFortyNavObserver

navController.addOnDestinationChangedListener(LinkFortyNavObserver())

LinkFortyNavObserver requires androidx.navigation (which your app already has if it uses Jetpack Navigation); the SDK depends on it only as compileOnly.

Manual — call it yourself (e.g. for screens not driven by a NavController):

LinkForty.shared.trackScreenView("ProductDetail")
val result = LinkForty.shared.createLink(
CreateLinkOptions(
deepLinkParameters = mapOf("route" to "PRODUCT", "id" to "123"),
title = "Check out this product",
utmParameters = UTMParameters(source = "app", medium = "share")
)
)

println(result.url) // https://go.yourdomain.com/abc123
println(result.shortCode) // abc123
warning

Link creation requires an API key. The simplified endpoint (/api/sdk/v1/links) is only available on LinkForty Cloud, not self-hosted Core.


AndroidManifest.xml​

Add an intent filter to your main activity:

<activity
android:name=".MainActivity"
android:launchMode="singleTask">

<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="acme.toapp.me" />
</intent-filter>
</activity>

Use your LinkForty domain as the host, and add a second <data> element for each custom domain. Your LinkForty server automatically hosts the Digital Asset Links file at /.well-known/assetlinks.json on every one of those hosts.

Important: onNewIntent​

Override onNewIntent() in your Activity to handle links when the app is already running. Without this, tapping a link while the app is in the background won't deliver the new intent data.


API Reference​

Initialization​

MethodDescription
LinkForty.initialize(context, config)Initialize the SDK with context and configuration

Deep Linking​

MethodDescription
onDeferredDeepLink(callback)Register callback for install attribution
onDeepLink(callback)Register callback for direct deep links
handleDeepLink(uri)Pass an incoming URI to the SDK

Event Tracking​

MethodDescription
trackEvent(name, properties)Track a custom in-app event
trackRevenue(amount, currency, properties)Track a revenue event (BigDecimal)
trackScreenView(name, properties)Report a screen view (for screen-flow funnels)

For automatic screen tracking with Jetpack Navigation, attach LinkFortyNavObserver to your NavController instead of calling trackScreenView per screen.

MethodDescription
createLink(options)Create a short link programmatically

User Identity​

MethodDescription
setExternalUserId(id)Set external user ID for share attribution (pass null to clear)
getExternalUserId()Get the current external user ID

Data Access​

MethodDescription
getInstallData()Retrieve cached attribution data
getInstallId()Get the server-assigned install UUID

Data Management​

MethodDescription
clearData()Wipe all locally stored SDK data
reset()Return SDK to uninitialized state

Offline Resilience​

Events are automatically queued when the network is unavailable:

  • Maximum queue size: 100 events
  • Queue persists across app restarts
  • Events are retried automatically when connectivity is restored

Privacy​

The Android SDK is designed with privacy in mind:

  • No GAID collection -- attribution uses probabilistic fingerprinting only
  • Minimal data collection -- timezone, language, screen resolution, Android version, app version, User-Agent
  • HTTPS enforced -- all communication uses HTTPS (except localhost for development)

Self-Hosted Configuration​

For self-hosted LinkForty Core, omit the API key:

val config = LinkFortyConfig(
baseURL = "https://links.yourcompany.com",
apiKey = null,
debug = false
)
LinkForty.initialize(this, config)

Troubleshooting​

  1. Verify android:autoVerify="true" is set on the intent filter
  2. Check that the Digital Asset Links file is accessible at https://go.yourdomain.com/.well-known/assetlinks.json
  3. Test with: adb shell am start -a android.intent.action.VIEW -d "https://go.yourdomain.com/test"
  4. Verify app is set as default handler in Settings > Apps > Default apps
  1. Check Logcat output (filter by "LinkForty") for install reporting errors
  2. Ensure the attribution window hasn't expired
  3. Fingerprint matching depends on network conditions -- VPNs reduce accuracy

ProGuard / R8​

If you're using code shrinking, the SDK should work without additional rules. If you encounter issues, add:

-keep class com.linkforty.sdk.** { *; }

Next Steps​

Resources​