Get started

Android SDK

Kotlin, callable from Java. The SDK depends only on Google's Play Install Referrer library and AndroidX lifecycle-process. Pro plan and up.

Requirements

  • Android 6.0+ (minSdk 23).
  • Kotlin or Java.
  • An SDK key: in the dashboard, open your app → Settings → SDK keys → New key. It's shown once, so copy it then.

Install

The SDK is on Maven Central, which every new Android project already uses. Add one line to the app module and sync Gradle:

// app/build.gradle.kts
dependencies {
    implementation("dev.unolink:unolink:0.3.3")
}

Configure

Call configure once, in your Application class, before anything else runs:

import dev.unolink.Unolink

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        Unolink.configure(this, "ulk_…")
    }
}

No Application class yet? Create this one and register it in AndroidManifest.xml:

<application android:name=".MyApp" …>

Java:

public class MyApp extends Application {
    @Override public void onCreate() {
        super.onCreate();
        Unolink.configure(this, "ulk_…");
    }
}

Options

Pass UnolinkOptions as the third argument. Every field is optional.

Unolink.configure(this, "ulk_…", UnolinkOptions(
    logLevel = LogLevel.DEBUG,
    debug = true,
))
OptionDefaultWhat it does
debugnull: true for debuggable buildsMarks this install as a test device. Dashboards hide test devices unless Include test devices is on.
logLevelLogLevel.WARNHow much goes to Logcat, tag Unolink: NONE, ERROR, WARN, INFO, DEBUG.
debugInstallReferrernullDebug builds only: pretend Google Play returned this install referrer. See Testing.
baseUrlhttps://api.unolink.devThe SDK API. Leave it as it is.

The key is public: it ships inside your app and can only report data for that one app. See SDK keys.

Funnel events

Add one line at each moment that matters. These become the steps in Funnel and Journeys.

Unolink.onboardingCompleted()                 // the last onboarding screen is done
Unolink.paywallViewed("onboarding")           // a paywall appeared; placement says which one
Unolink.paywallSkipped("onboarding")          // closed without buying
Unolink.signUp("email")                       // an account was created
Unolink.signUpSkipped()                       // went on as a guest (0.3.2+)

Without RevenueCat, report trials and purchases yourself. The amount is a BigDecimal, so prices never round:

Unolink.trialStarted(productId = "pro_yearly")
Unolink.purchase(BigDecimal("49.99"), "USD", productId = "pro_yearly")
Unolink.purchaseCancelled(productId = "pro_yearly")   // closed the purchase sheet
Unolink.purchaseFailed(productId = "pro_yearly", reason = "card_declined")

Your own events take a name and up to 20 properties:

Unolink.track("level_up", mapOf("level" to 3))

The full list, with the names that reach the dashboard, is in Events.

User ID and RevenueCat

If you use RevenueCat, connect it under your app → Settings → RevenueCat. Then don't call purchase or trialStarted: RevenueCat sends trials, purchases, renewals, cancellations and refunds itself, and Unolink ignores app purchase calls so nothing counts twice.

To join those purchases to the install, report RevenueCat's own app user ID. With SDK 0.3.2 or later, one line keeps it current. Call it once at launch, before or after configure:

Unolink.connectRevenueCat { Purchases.sharedInstance.appUserID }

Unolink reads the ID at launch, whenever the app comes to the foreground and before each event, and sends it only when it changed. If RevenueCat isn't configured yet, the read fails quietly and is tried again later. After Purchases.sharedInstance.logIn and logOut, call Unolink.revenueCatUserChanged() to send the new ID at once.

Without it (or before 0.3.2), call setUserId with RevenueCat's ID after Purchases.configure, after every Purchases.logIn and Purchases.logOut, and on every launch. Use one or the other, not both:

// after Purchases.configure, logIn and logOut, and on every launch
Unolink.setUserId(Purchases.sharedInstance.appUserID)

For a guest, that's RevenueCat's anonymous ID, so a purchase made before signing up joins the install right away. After logIn, it's the account's ID. Every ID an install has reported counts, so the guest's purchase stays joined. See Guests and accounts.

Unolink never stores the ID itself, only a one-way hash that's different for each of your apps.

When someone taps a Unolink link on Android, installs from Google Play and opens the app, the SDK reads the Play install referrer and calls your listener once, on the main thread. Set the link's Deep link and Parameters in the dashboard (Links → ⋯ → Edit).

Unolink.setDeferredDeepLinkListener { link ->
    // link.deepLink: "/promo/3for2" or "myapp://promo/3for2", or null
    // link.params:   mapOf("offer" to "3for2")
    // link.linkSlug: "tiktok", or null for your app's main link
    // link.clickId:  "clk_01JA…"
    link.deepLink?.let { navigate(it) }
    Unolink.consumeDeferredDeepLink()
}

The listener is called once per install. Register it right after configure, or later: a link that arrived before you registered is still delivered, on a later launch too if no listener was set before.

Open it after onboarding

First opens usually start in onboarding. If the link's screen sits behind onboarding or sign-in, don't open it from the listener. When onboarding (or sign-in) is done, read the pending link, open it, then consume it:

// when onboarding (or sign-in) is done, and on every launch after that until it's consumed
Unolink.getPendingDeferredDeepLink()?.let { link ->
    link.deepLink?.let { navigate(it) }
    Unolink.consumeDeferredDeepLink()
}

The link waits until you consume it, even if the app is closed in between (up to 30 days after the install; 0.3.3+). A new user who quits halfway through onboarding still gets it on the next launch, so you don't need to store it yourself. Call consumeDeferredDeepLink() once you've opened it: until then, getPendingDeferredDeepLink() returns it on every launch. It's null before configure and until Google Play has answered (a second or two on the first launch).

To test without a Play install, use debugInstallReferrer (see Testing). How the referrer works is in Android deferred deep links.

Other calls

CallWhat it does
Unolink.getInstallId()The random ID of this install, or null before configure. It identifies no person or device.
Unolink.flush()Sends queued events now. Normally not needed.
Unolink.setEnabled(false)Turns tracking off and deletes queued events, for your opt-out setting. true turns it back on. Remembered across launches.

How it behaves

  • First launch: reads the Play install referrer, registers the install, then delivers the deep link if there is one.
  • Automatic events: first_open, session_start (on launch, or after 30 minutes in the background) and app_background. They're free.
  • Events are stored on the device (up to 1,000) and sent in batches every 15 seconds while the app is open, and when it goes to the background. Offline, they wait and retry.
  • It never blocks, never throws and never uses the Advertising ID.

Test devices

Debuggable builds are test devices automatically. Their data is real but hidden until you turn on Include test devices in Funnel and Journeys. More in Testing.

Privacy

  • A random install ID, stored only in your app's own storage. A reinstall gets a new one.
  • App version, OS version, SDK version, phone or tablet, language. Country comes from the network edge. IP addresses are never stored.
  • No Advertising ID, no fingerprinting.
  • Never put names, emails or other personal data in event properties.

Troubleshooting

  • Nothing in the dashboard? Turn on Include test devices: debug builds are hidden by default.
  • Still nothing? Configure with UnolinkOptions(logLevel = LogLevel.DEBUG) and filter Logcat by Unolink. A wrong or revoked key is logged as an error.
  • No deep link? It only arrives for installs from Google Play through a Unolink link with a deep link set, on the first launch. Reinstall to test again, or use debugInstallReferrer.

More in Troubleshooting.