Back to all posts

Mobile Apps · Android · Play Store · September 2026 · 11 min read

How to run an affiliate program for an Android app in 2026 (App Links + Install Referrer)

The Android side is, mercifully, easier than iOS. Play has a real install-referrer API. App Links auto-verify on Android 12+. Here's the architecture that actually works, plus the assetlinks.json gotchas that silently break attribution if you ignore them.

If you've already read our companion piece on iOS affiliate programs, you know the iOS situation: the App Store sits between the click and the install, IDFA is effectively dead, and the only way to bridge tap to install is Universal Links plus a subscription-provider custom attribute. Android shares 80% of that architecture but has one large advantage: the Play Store actually has an attribution API. Google gives developers what Apple has spent five years deprecating.

That doesn't make Android trivial. The Play Install Referrer API has a specific window, App Links have a verification step most teams skip, and the assetlinks.json file has a SHA-256 keystore gotcha that breaks attribution silently until the merchant ships a release build. We'll walk through all of it.

1. Why Android affiliate is easier than iOS (but still not trivial)

The web-grade affiliate model assumes cookies plus referer headers. Like iOS, native Android breaks both. The Play Store sits between the click and the install. Cookies don't cross the Play Store boundary. The Custom Tabs cookie jar is sandboxed from your app the same way Safari View Controller's is on iOS.

But Android has two things iOS doesn't.

The Play Install Referrer API. When a user taps a Play Store URL with a &referrer= parameter (or arrives via Google Ads / Search Ads), the Play Store remembers that string. After install, your app can call InstallReferrerClient and Google returns the referrer string and the timestamps for click and install. It's the cleanest deferred-deep-link mechanism on any consumer OS. The catch: the referrer string is capped at 1024 chars, you have one shot to read it (Google purges after 90 days or first reinstall), and you have to call it on first launch.

App Links auto-verification. Android 12 introduced automatic verification of App Links. If you ship assetlinks.json on your domain and your app's manifest declares the right intent filter, every install on Android 12+ will verify ownership automatically and open links in your app instead of the browser. No user prompt. The verification fingerprint check is stricter than iOS Universal Links, which is both good (no spoofing) and bad (one mismatched keystore breaks it silently).

Between Install Referrer for the deferred case and App Links for the warm case, Android covers the same install funnel iOS covers with Universal Links, only with a more honest API. The trade-off is that you have to wire both, where on iOS you can lean on Universal Links to cover both warm and deferred via a single mechanism.

GAID (Google Advertising ID) has followed IDFA's trajectory more slowly. Google added user-resettable GAID in 2013 and a "delete advertising ID" option in 2022. Effective opt-out is higher than people remember, around 50% in privacy-aware markets. Don't build attribution on GAID; treat it as a fallback signal at best.

2. What doesn't work

Clipboard handoff

Same problem as iOS. Android 12 added a clipboard read indicator (toast); Android 13 surfaced explicit notifications. Read rates dropped, user trust dropped. Don't.

Typed referral codes

Same redemption ceiling as iOS. 4 to 8%. Works as a fallback for users who somehow lose all attribution, not as a primary mechanic.

Custom URL schemes

App Links replaced URL schemes for the same reasons iOS Universal Links replaced theirs. Custom schemes don't verify domain ownership, so Android will show a chooser if more than one app claims the same scheme. The user picks the wrong one and your attribution is lost. Use App Links.

Server-side IP fingerprinting

Same accuracy problem as iOS. CGNAT on cellular tunnels tens of thousands of phones through a few IPs. Match rates on cellular networks are sub-5%.

MMPs (Branch, AppsFlyer, Singular, Adjust)

Same story as the iOS post. They work, they're built for paid-acquisition spend, they cost $500-$2,000/month minimum. If you're an indie founder launching an affiliate program with creators earning $50-$200/month each, the MMP fixed cost eats the channel's first 12-18 months of margin.

3. What works in 2026 on Android

The chain is the same shape as iOS, with Install Referrer doing some of the work App Links can't.

Hop 1: Smart link with the Play Store as the fallback

The affiliate link is a domain you own: https://yourbrand.com/r/jane10. Android checks https://yourbrand.com/.well-known/assetlinks.json for ownership. If the user has your app installed AND your app is App Links-verified on their device, Android opens the app directly with the URL. No browser, no chooser.

If the user doesn't have the app, your server-side redirect sends them to:

https://play.google.com/store/apps/details
    ?id=com.yourbrand.app
    &referrer=affref_code%3Djane10

The referrer parameter is URL-encoded. Anything that fits in 1024 chars and parses as an opaque string. Convention is key=value pairs joined by &.

The user lands on the Play Store, taps Install, downloads your app. Google's servers remember the referrer string associated with this device for this app for up to 90 days.

Hop 2: First launch, the app reads the referrer

Add the dependency:

// build.gradle.kts
implementation("com.android.installreferrer:installreferrer:2.2")
implementation("com.affref:affref-android:0.1.0")

On first launch, before the user can subscribe, call Install Referrer:

import com.android.installreferrer.api.InstallReferrerClient
import com.android.installreferrer.api.InstallReferrerStateListener
import com.affref.AffRef

val client = InstallReferrerClient.newBuilder(context).build()
client.startConnection(object : InstallReferrerStateListener {
    override fun onInstallReferrerSetupFinished(responseCode: Int) {
        if (responseCode == InstallReferrerClient.InstallReferrerResponse.OK) {
            val referrer = client.installReferrer.installReferrer
            AffRef.handleInstallReferrer(referrer) // parses affref_code=...
        }
        client.endConnection()
    }
    override fun onInstallReferrerServiceDisconnected() {}
})

Also wire App Links for the warm case (user already has the app and taps the affiliate link in a browser):

// MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    intent.data?.let { AffRef.handle(it) }
}

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

The SDK normalizes the code from whichever signal fired (Install Referrer for first-install attribution, App Link intent for warm taps) and exposes it through one callback.

Hop 3: Hand the code to the subscription stack

Same as iOS. The conversion event comes from your subscription provider's webhook, not from your app. You have to set the affiliate code as a user attribute before the user subscribes.

// RevenueCat
AffRef.onCodeCaptured { code ->
    Purchases.sharedInstance.attribution.setAttributes(
        mapOf("affref_code" to code)
    )
}
// Adapty
AffRef.onCodeCaptured { code ->
    val params = AdaptyProfileParameters.Builder()
        .withCustomAttribute("affref_code", code)
        .build()
    Adapty.updateProfile(params) { /* result */ }
}

Hop 4: Subscription webhook closes the loop

When the user subscribes, RevenueCat/Adapty fires its webhook with affref_code attached to the event. Your affiliate platform matches and creates the conversion. Same as the iOS chain. Same trial-policy and refund-reversal logic.

4. The assetlinks.json gotcha

App Links verification requires a Digital Asset Links file at https://yourbrand.com/.well-known/assetlinks.json that lists every SHA-256 fingerprint Android should accept as your app. Here's the format:

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.yourbrand.app",
    "sha256_cert_fingerprints": [
      "14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
    ]
  }
}]

The trap: that fingerprint comes from your release keystore. Most developers test with the debug keystore Android Studio generates automatically. The debug fingerprint is different. If you ship to the Play Store with the release keystore but your assetlinks.json lists the debug fingerprint (or vice versa), App Links silently fail to verify on every install. The user taps the affiliate link, sees a browser chooser instead of opening the app, and your attribution dies.

Three ways to get this right:

  1. List both fingerprints. Debug AND release. The file is just a JSON array; you can include multiple fingerprints in sha256_cert_fingerprints. Belt and suspenders.
  2. Use Play App Signing. Google holds the signing key. Get the fingerprint from Play Console → Setup → App Signing. This is the canonical answer for any app uploaded to the Play Store after 2021.
  3. Verify in production. After release, install on a test device and visit adb shell pm verify-app-links --re-verify {package_name}. Logcat will show the verification result. Alternatively check Settings → Apps → {your app} → Open by default; if "Supported web addresses" lists your domain with the "Verified" badge, you're good.

Don't ship without verifying this. Half the "App Links don't work" bug reports on Stack Overflow are this exact mismatch.

5. The race condition still applies

The Android chain has the same race the iOS chain has. User opens app, taps Subscribe within seconds, subscription webhook fires before the SDK has finished pushing affref_code to RevenueCat or Adapty.

Solution is identical. Your affiliate backend records an install row at the moment Install Referrer or App Links fires. When a subscription webhook arrives without affref_code attached, the backend looks up the install row by device identifier (the same ID the subscription provider sends as app_user_id or customer_user_id) within an attribution window and applies the stored affiliate code as a fallback.

If you skip this and your paywall fires on first launch, expect to lose 20-30% of legitimate attributions to the race. The fallback is mandatory for any app with a fast subscribe path.

6. End-to-end lifecycle

  1. Jane (creator) gets the smart link https://yourbrand.com/r/jane10 from her affiliate dashboard. She posts it.
  2. Sam taps it on his Android phone. Chrome opens (no app installed yet).
  3. Your server-side handler 302-redirects to play.google.com/store/apps/details?id=com.yourbrand.app&referrer=affref_code%3Djane10. Sam lands on the Play Store, taps Install.
  4. Sam opens the app. The SDK calls Install Referrer on first launch, gets affref_code=jane10 from Google, posts an install row to AffRef with Sam's RevenueCat app_user_id, and sets affref_code = jane10 as a RevenueCat subscriber attribute.
  5. Three minutes later, Sam subscribes.
  6. RevenueCat fires its webhook to AffRef with subscriber_attributes.affref_code: "jane10".
  7. AffRef matches and creates the Conversion. Commission calculated, queued for review or auto-approved depending on settings.
  8. Jane sees the conversion in her brand-portal dashboard. After the refund window clears, the payout becomes payable.

If Sam subscribed in 5 seconds instead of 3 minutes, the AffRef install-row fallback (section 5) catches it: when the webhook arrives without an affref_code, AffRef looks up the install row by Sam's app_user_id, finds jane10, credits Jane.

7. Vendor-evaluation checklist

  1. Does the platform host the assetlinks.json file for you, or are you on the hook? If they host, you point your .well-known path at theirs via a CNAME or reverse-proxy. If you host, you have to remember to update it every time you add a new build variant.
  2. Does the SDK call Install Referrer for you, or do you have to wire InstallReferrerClient by hand? Wiring it by hand is fine but you need to handle FEATURE_NOT_SUPPORTED (older Play Store versions), SERVICE_UNAVAILABLE (rare), and the one-shot purge behavior.
  3. Does it support both release and debug keystore fingerprints in the assetlinks file? You'll want debug for internal testing.
  4. Does it integrate with RevenueCat / Adapty / Qonversion natively, or expect you to fire conversion calls from your billing handler? Manual is brittle.
  5. Does it implement the install-row fallback for the race condition? Same diligence question as iOS.
  6. Pricing model. Flat-fee SaaS, per-conversion, or per-install? Per-install pricing is MMP-style and aggressive. For an affiliate program (not paid acquisition), flat-fee fits better.

So how does AffRef do it

Everything in section 3 plus the assetlinks.json hosting (including both debug and release fingerprints per build variant), the Install Referrer wiring, the App Links auto-verification helper, the RevenueCat and Adapty webhook handlers, the install-row fallback for the race condition, and the trial-policy toggle. Same dashboard your iOS and (Shopify, SaaS) brands share.

One Pro-plan subscription, no per-conversion fee, no MMP contract. Plans & pricing · Full mobile SDK setup · The iOS companion piece · Start the trial.

If you ship Android and have been holding off on an affiliate program because the only platforms that did it priced you out, the math is different now. Build it, run it for a month, and let the channel either prove itself or fall off the funnel naturally. The platform shouldn't be the reason you don't try.

Ready to grow your affiliate program?

Start your 7-day free trial. Cancel anytime.

View Plans
Usually replies within an hour
Hey 👋 I'm Aaron, founder of AffRef. Drop your email and your question and I'll get back to you fast.
By chatting you agree to our privacy policy.
End this chat? History on this device clears.