# Kotlin SDK

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

export const components = {
  pre: CodeSnippet,
};

The WinWinKit Kotlin SDK is a thin, suspend-based wrapper around the REST API for Android apps. Every call returns an [`ApiResult`](#error-handling) 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](https://jitpack.io/).

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

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

Add the dependency to your module's `build.gradle.kts` (replace `<tag>` with the desired [release tag](https://github.com/winwinkit/winwinkit-kotlin/releases)):

```kotlin
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](/developers/get-started/api-keys/) from the WinWinKit dashboard (**Settings** → **Integrations** → **API Keys**).

```kotlin
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.

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

<Warning>
  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.
</Warning>

<Warning>
  Calling any method while `appUserId` is `null` throws
  `IllegalStateException`.
</Warning>

<Tip>
  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.
</Tip>

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

```kotlin
winwinkit.appUserId = null
```

## Create a user

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

```kotlin
val result = winwinkit.createOrUpdateUser()
```

All arguments are optional. To set writable fields at creation time, see [User properties](#updatable).

## 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.

```kotlin
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.

```kotlin
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.

```kotlin
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.

<Tip>
  Is updated automatically when revenue tracking is configured — directly from
  App Store, or Google Play, and from RevenueCat, Stripe. Configure them in
  **Settings** → **Integrations**.
</Tip>

#### Is Trial

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

<Tip>
  Is updated automatically when revenue tracking is configured — directly from
  App Store, or Google Play, and from RevenueCat, Stripe. Configure them in
  **Settings** → **Integrations**.
</Tip>

#### First Seen At

The "first seen at" date controls [claim code eligibility](/docs/users/introduction/#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.

```json
"referral_code": "ABC123"
```

#### Referral Code Link

A hosted [Code Link](/docs/pages/code-link/) URL to the user's referral code.

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

#### Claim Code Eligibility

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

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

See [Claim Code Eligibility](/docs/users/introduction/#claim-code-eligibility) for the rules.

#### Referred By

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

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

#### Stats

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

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

#### Rewards

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

```json
{
  "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.

```kotlin
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.

```kotlin
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.

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

### Refresh

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

```kotlin
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`:

```kotlin
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](/developers/api/errors/) for the full list of error codes the API can return.
