Kotlin SDK

Learn how to install, configure, and use our Kotlin SDK.

The WinWinKit Kotlin SDK is a thin, suspend-based wrapper around the REST API for Android apps. Every call returns an ApiResult so successes, errors, and unexpected exceptions are explicit at every call site.

Installation

Requirements

  • JDK 21+
  • Kotlin 1.9.23+
  • Gradle 8.7+

Gradle (JitPack)

The SDK is distributed via JitPack.

Add the JitPack repository to your root build.gradle.kts:

repositories {
    maven("https://jitpack.io")
}

Add the dependency to your module’s build.gradle.kts (replace <tag> with the desired release tag):

dependencies {
    implementation("com.github.winwinkit:winwinkit-kotlin:<tag>")
}

You can now import com.winwinkit.WinWinKit from any source file.

Configuration

Instantiate WinWinKit once with your API key. Use an API Key from the WinWinKit dashboard (SettingsIntegrationsAPI Keys).

import com.winwinkit.WinWinKit

val winwinkit = WinWinKit(apiKey = "your-api-key")

Hold on to this instance — typically as an injected singleton or a top-level property — and reuse it across the app.

Identify the user

Set the app user id before calling any method.

winwinkit.appUserId = "your-app-user-id"

Use a UUID-like value for the app user id — never an email, phone number, username, or any other personally identifying information. The id ends up in URLs, logs, and analytics, so treat it as a public identifier.

Calling any method while appUserId is null throws IllegalStateException.

If your app has no sign-in flow, generate a random UUID on first launch and persist it in EncryptedSharedPreferences (preferred) or SharedPreferences as a simpler fallback.

When the user logs out, clear the app user id so any subsequent call doesn’t get attributed to the previous user:

winwinkit.appUserId = null

Create a user

Register the user in WinWinKit with createOrUpdateUser(...) before calling claimCode or any other user related action.

val result = winwinkit.createOrUpdateUser()

All arguments are optional. To set writable fields at creation time, see User properties.

Claim a code

Call claimCode(code) when a user enters a code in your app — typically from a “Got a referral code?” screen, a referral link landing flow, or a deep link handler.

Pass the code exactly as the user entered it — WinWinKit validates it and returns a Failure if it’s unknown, expired, ineligible, or already claimed. On success the response includes the updated User and any rewards granted at claim time.

when (val result = winwinkit.claimCode("XYZ123")) {
    is ApiResult.Success -> {
        val user = result.data.user
        val rewardsGranted = result.data.rewardsGranted
        // Apply granted rewards in your app.
    }
    is ApiResult.Failure -> {
        // Show errors from result.errors — see Error handling below.
    }
    is ApiResult.Exception -> {
        // Network I/O or parse failure — result.cause has details.
    }
}

Grant user rewards

The User object is the source of truth for what the user is entitled to in WinWinKit — claimed codes, active rewards, and stats. Fetch it after the user is identified (and whenever you want a fresh snapshot) to unlock the matching perks in your app.

val user = (winwinkit.fetchUser() as? ApiResult.Success)?.data
// Inspect user?.rewards?.active and grant features accordingly.

What you do with each reward depends on its type:

  • Basic — flip a feature flag or unlock content in your app.
  • Credit — apply the balance; call withdrawCredits when the user spends it.
  • App Store Offer Code / Google Play Promo Code — present the code to the user or trigger the platform redemption flow.
  • RevenueCat Entitlement — read the entitlement state from RevenueCat and unlock the corresponding feature.
  • RevenueCat Offering — display the matching paywall offering.

The SDK is stateless — it doesn’t cache the user. Store the result yourself (e.g. in a StateFlow) so your UI can react when it changes.

User object

The User object exposes properties you can update from the SDK and properties WinWinKit manages for you.

Updatable

Update writable fields with createOrUpdateUser(...). All arguments are optional — pass only what you want to change.

winwinkit.createOrUpdateUser(
    isPremium = true,
    isTrial = false,
    firstSeenAt = OffsetDateTime.now(),
    metadata = mapOf("plan" to "annual", "platform" to "android"),
)

Is Premium

Marks the user as a paying customer. Drives conversion tracking, affiliate commissions, and reward activation.

Is updated automatically when revenue tracking is configured — directly from App Store, or Google Play, and from RevenueCat, Stripe. Configure them in SettingsIntegrations.

Is Trial

Marks the user as currently on a free trial. Conversions are not counted while a user is in trial.

Is updated automatically when revenue tracking is configured — directly from App Store, or Google Play, and from RevenueCat, Stripe. Configure them in SettingsIntegrations.

First Seen At

The “first seen at” date controls claim code eligibility (default window: 7 days). If you don’t set it, WinWinKit uses the user’s creation date.

Metadata

Attach arbitrary key-value pairs to a user.

Provided by WinWinKit

These properties are managed by WinWinKit. Read them from the User returned by fetchUser() or createOrUpdateUser(...).

Referral Code

The user’s own referral code, generated when they are created in WinWinKit.

"referral_code": "ABC123"

A hosted Code Link URL to the user’s referral code.

"referral_code_link": "https://example.wwk.link/ABC123"

Claim Code Eligibility

Whether the user can claim a code right now, and until when.

{
  "eligible": true,
  "eligible_until": "2026-05-05T12:00:00Z"
}

See Claim Code Eligibility for the rules.

Referred By

The code that referred this user, if any. type is one of affiliate, promo, or referral.

{
  "code": "XYZ123",
  "type": "referral"
}

Stats

Counters for users who claimed this user’s referral code: total claims, conversions to premium, and subsequent churns.

{
  "claims": 12,
  "conversions": 5,
  "churns": 1
}

Rewards

The user’s active and expired rewards, grouped by reward type.

{
  "active": {
    "basic": [...],
    "credit": [...],
    "offer_code": [...],
    "googleplay_promo_code": [...],
    "revenuecat_entitlement": [...],
    "revenuecat_offering": [...]
  },
  "expired": {
    "basic": [...],
    "credit": [...],
    "offer_code": [...],
    "googleplay_promo_code": [...],
    "revenuecat_entitlement": [...],
    "revenuecat_offering": [...]
  }
}

Referral Program

The referral program the user is part of, if any.

Methods

Withdraw credits

Spend down a credit reward balance. The amount is debited from the user’s available credit for the given key. operationId is optional and lets you make the call idempotent — passing the same id twice debits only once.

val result = winwinkit.withdrawCredits(
    key = "extra-levels",
    amount = 5,
    operationId = "checkout-7d3a",
)

Register App Store transaction

Register an App Store transaction with WinWinKit. Only needed when using direct App Store revenue tracking (without RevenueCat integration) — call this after a purchase completes so WinWinKit can attribute the conversion without delays.

val result = winwinkit.registerAppStoreTransaction(
    originalTransactionId = "1000000123456789",
    appAccountToken = null,
)

Register Google Play transaction

Register a Google Play purchase with WinWinKit. Only needed when using direct Google Play revenue tracking (without RevenueCat integration) — call this after a purchase completes so WinWinKit can attribute the conversion without delays.

val result = winwinkit.registerGooglePlayTransaction(
    purchaseToken = "abc...token",
    obfuscatedExternalAccountId = null,
)

Refresh

To refresh the user’s state, call fetchUser() again.

val result = winwinkit.fetchUser()

Error handling

Every method returns a sealed ApiResult with three cases:

  • ApiResult.Success<T>(data) — 2xx response with the parsed body.
  • ApiResult.Failure(errors) — 4xx or 5xx response. errors is a list of ErrorObject parsed from the response body (empty if the body could not be parsed).
  • ApiResult.Exception(cause) — network I/O failure, JSON parse error on a 2xx body, or any other unexpected throwable. cause preserves the original exception.

Handle all three cases with an exhaustive when:

when (val result = winwinkit.claimCode("XYZ123")) {
    is ApiResult.Success -> println(result.data.user.referralCode)
    is ApiResult.Failure -> println(result.errors)
    is ApiResult.Exception -> println("unexpected: ${result.cause}")
}

fetchUser() returns Success(null) when the user does not exist (HTTP 404); all other non-2xx responses return Failure.

CancellationException is not caught — it propagates per coroutine conventions. IllegalStateException (raised when appUserId is null) is also not wrapped — it surfaces directly.

See Errors for the full list of error codes the API can return.