Android SDK
Integrate the WarpLink Android SDK with Gradle to add App Links, deferred deep links, and install attribution to your app.
Requirements: Android API 26+ (Android 8.0+), Kotlin 2.1+, compileSdk 35+. A physical device is recommended for App Links verification.
Source: github.com/WarpLinkApp/warplink-android-sdk
Installation
Add the dependency to your app's build.gradle.kts:
dependencies {
implementation("app.warplink:sdk:1.1.1")
}
Sync your project with Gradle files.
Configure the SDK
Initialize WarpLink in your Application.onCreate(). Pass an onLink callback and configure() auto-registers cold-start handling and auto-fires the deferred deep link check. Your one callback receives every resolved link.
The credential below is an SDK key, created in the dashboard under Keys > SDK keys. Configure the SDK with an SDK key, since only an SDK key records installs. If your organization has several apps, create the key for this app: a key tied to one app is refused for any other app with KEY_APP_MISMATCH.
import android.app.Application
import app.warplink.WarpLink
import app.warplink.WarpLinkOptions
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
WarpLink.configure(
context = this,
apiKey = "wl_live_yoursdkkeyhere000000000000000000",
options = WarpLinkOptions(onLink = { result ->
result.onSuccess { deepLink ->
// Fired for taps AND deferred installs. Check deepLink.isDeferred.
navigateTo(deepLink)
}
})
)
}
}
Register your Application class in AndroidManifest.xml and grant internet access:
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".MyApp"
... >
Warm Start Needs One Line
Cold start is automatic. For warm start (app already running when a link arrives), forward the new intent from your launch Activity:
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
WarpLink.onNewIntent(intent)
}
Set android:launchMode="singleTask" on that Activity so warm-start links reuse the existing instance.
Opt Out of Automatic Handling
WarpLink.configure(
context = this,
apiKey = "wl_live_yoursdkkeyhere000000000000000000",
options = WarpLinkOptions(
debugLogging = true, // WarpLink Logcat tag
automaticDeepLinks = false, // you call WarpLink.handleDeepLink yourself
automaticDeferredDeepLinks = false // you call WarpLink.checkDeferredDeepLink yourself
)
)
Add App Links Intent Filter
Add the intent filter to your main Activity in AndroidManifest.xml:
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- WarpLink App Links -->
<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="yourapp.aplnk.to" />
</intent-filter>
</activity>
Find your app's subdomain in the dashboard: open the app, then Settings. Or see Your app's own link address. The android:autoVerify="true" attribute triggers automatic assetlinks.json verification at install time.
Serving links from a custom domain? Add a second <data> line for it (android:host="links.yourapp.com"), then declare it to the SDK as below.
Every app has its own subdomain, yourapp.aplnk.to. Add that exact host, never a wildcard such as *.aplnk.to. If your app existed before subdomains, keep aplnk.to listed next to the new address: links you already have stay on aplnk.to and keep opening your app. See Your app's own link address.
Declare Your Link Domains
aplnk.to is recognized out of the box. Your app's subdomain, yourapp.aplnk.to, and any custom domain need to be named explicitly so a link on them resolves the moment the app launches, rather than waiting on a network round trip to find out.
The reason is timing, not policy. ActivityLifecycleCallbacks inspects the launch intent as soon as the Activity is created, which on a fresh install happens before the SDK's own /sdk/validate call has had a chance to return anything. A host you declare in code or in the manifest is recognized at that moment.
Declare them in code:
WarpLink.configure(
context = this,
apiKey = "wl_live_yoursdkkeyhere000000000000000000",
options = WarpLinkOptions(
linkDomains = listOf("yourapp.aplnk.to", "links.yourapp.com", "go.yourapp.com"),
onLink = { result -> result.onSuccess { navigateTo(it) } }
)
)
Or declare them with no code at all, in AndroidManifest.xml. The value is a comma separated list, inside <application>:
<meta-data
android:name="app.warplink.DOMAINS"
android:value="yourapp.aplnk.to,links.yourapp.com,go.yourapp.com" />
Both forms are optional and both are additive. The SDK uses the union of aplnk.to, linkDomains, the manifest list, and the domains it learns from the server. Declare your app's subdomain so a link on it resolves on a fresh install.
You don't need to pre-clean either list. Whitespace is trimmed, casing is flattened, and a full URL like https://Links.YourApp.com/ is reduced down to its bare host before comparison, so it and links.yourapp.com are treated identically. A www. prefix survives that normalization on purpose: www.yourapp.com and yourapp.com are two different hosts as far as an incoming Android URI is concerned, and collapsing them would be wrong for an app that only owns one of the two.
Get Your SHA256 Fingerprint
Register your signing key fingerprint in the WarpLink dashboard for App Links verification.
keytool -list -v -keystore ~/.android/debug.keystore \
-alias androiddebugkey -storepass androidkeytool -list -v -keystore your-release-key.keystore -alias your-aliasCopy the SHA256 value and paste it into the dashboard under Apps.
Test App Links on a Physical Device
# Verify App Links status
adb shell pm get-app-links com.yourcompany.yourapp
# Open a test link
adb shell am start -a android.intent.action.VIEW \
-d "https://yourapp.aplnk.to/abc123" com.yourcompany.yourapp
# Filter Logcat for WarpLink messages
adb logcat -s WarpLink
How the SDK Is Built
Two facts about the Android SDK shape the integration choices above.
The API is callback-based. configure(), handleDeepLink(), and checkDeferredDeepLink() take a plain lambda and return nothing you await, so configure() runs from Application.onCreate() before your app has a coroutine scope. Every callback lands on the main thread, so it is always safe to touch views or navigate from it directly. If your codebase is coroutine-first, wrap a call in suspendCancellableCoroutine at the call site.
WarpLink is a Kotlin object, not a class you instantiate. There is one SDK instance per process, matching the singleton WarpLink static type on iOS and the module-level export on React Native. configure() can be called again later (for example, after a user signs out and a new session needs a different key), and the second call safely tears down the first: any deferred check still in flight is abandoned, not left to deliver a result to callbacks that no longer expect one.
ProGuard and R8
No consumer setup is required. The SDK ships consumer-rules.pro, which Gradle applies to your app automatically through consumerProguardFiles, and it keeps every public class (WarpLink, WarpLinkOptions, WarpLinkDeepLink, WarpLinkError and its subtypes, MatchType). The SDK does not use reflection internally, so minification and R8 full mode do not break resolution or attribution on their own. If a minified build still misbehaves, the more likely cause is elsewhere: -dontwarn blanket rules stripping something your own networking stack needs, or a class name showing up de-obfuscated in a stack trace you're trying to symbolicate.
Play Install Referrer
The only library the Android SDK links is Google's Play Install Referrer client (com.android.installreferrer:installreferrer:2.2), a platform library from Google. There are no other dependencies, and Gradle pulls it in automatically. It backs the deterministic side of install attribution: a WarpLink URL that redirects to the Play Store passes the link ID through the Play referrer string, and the SDK reads it back at first launch with a two-second timeout before falling back to probabilistic matching. The SDK falls back to probabilistic matching automatically, with no code change on your side. See Attribution for the full match cascade and confidence scoring.
Requirements in Detail
minSdk = 26 (Android 8.0) is a hard floor: the SDK will not compile into an app targeting lower. compileSdk = 35 reflects the API surface the SDK's own source references; your app's compileSdk can be equal to or newer. App Links verification is the one piece that genuinely needs a physical device: the Play Store connection and the domain-ownership check the OS performs both behave inconsistently on emulators without Google Play Services installed, even when everything else about the integration is correct.
Troubleshooting
Tapping a link opens a chooser instead of your app. This is almost always one of three things: android:autoVerify="true" missing from the intent filter, a SHA256 fingerprint in the dashboard that does not match the keystore that actually signed the build, or an app that was never registered under Apps in the dashboard so assetlinks.json has nothing to serve. Confirm with adb shell pm get-app-links com.yourcompany.yourapp before changing anything: a status of verified next to yourapp.aplnk.to means the OS already trusts the link, and the bug is somewhere else in your routing.
Deep links resolve but no installs show up. Check which credential you passed to configure(). An API key and an SDK key share the same wl_live_ + 32-character shape, so a swapped key does not fail loudly: /sdk/validate still succeeds and links still open, but the attribution call is rejected every time. Create a dedicated SDK key under Keys > SDK keys and use it only for mobile builds.
See the full troubleshooting reference in the SDK repository for assetlinks.json verification and Logcat messages.
FAQ
Does the SDK support Jetpack Compose? Yes. WarpLink.configure() and onNewIntent are plain Kotlin calls with no dependency on the View system, so they work identically in a ComponentActivity using setContent {} and in a classic AppCompatActivity. See Deep Links for both shapes side by side.
Do I need android:launchMode="singleTask"? Only if you want warm-start links (the app already running) to reach onNewIntent. Without it, Android spins up a second Activity instance for the incoming link, and the SDK never sees it. Cold start does not depend on the launch mode at all.
Can I use the SDK without Google Play Services? Yes, for deep linking and probabilistic attribution. Only the deterministic Play Install Referrer tier requires Play Services; devices on Huawei's HMS or sideloaded builds fall back to probabilistic matching automatically.
Does WarpLink work with multiple Activities handling the same intent filter? Register the App Links intent filter only on the Activity you want the OS to launch (typically your single-Activity or navigation-host Activity). A second Activity with the same filter creates the disambiguation dialog App Links exist to avoid.
What happens if configure() is called twice? The second call replaces the first. Any deferred deep link check still in flight from the first configuration is abandoned rather than delivered to the new callback, which matters if you reconfigure with a different API key after a login or organization switch.
Next Steps
- Deep Links: Handle App Links in Activity and Compose
- Deferred Deep Links: Preserve context through Play Store installs
- Attribution: Play Install Referrer and probabilistic matching
- API Reference: Full Kotlin API documentation
iOS API Reference
The complete Swift API reference for the WarpLink iOS SDK: initialization, deep link handling, and attribution methods, with signatures and examples.
Android Deep Links
Handle Android App Links to route taps into the right screen, with cold start and warm start covered by the WarpLink SDK.