# Dart SDK

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

export const components = {
  pre: CodeSnippet,
};

The WinWinKit Dart SDK is a Dio-based wrapper around the REST API for Flutter and Dart apps. It exposes a single `WinWinKit` entry point and a per-user scoped `UserClient` so calls automatically carry the correct `appUserId`.

## Installation

### Requirements

- Dart 2.15+ / Flutter 2.8+
- Dio 5.0.0+ ([pub.dev](https://pub.dev/packages/dio))

### Package

The SDK is published to [pub.dev](https://pub.dev/packages/winwinkit) as `winwinkit`. Add it to your project:

**Flutter**

```bash
flutter pub add winwinkit
```

**Dart**

```bash
dart pub add winwinkit
```

You can now `import 'package:winwinkit/api.dart'` (or `package:winwinkit/winwinkit.dart` for the umbrella export) 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**).

```dart
import 'package:winwinkit/api.dart';

final sdk = WinWinKit(apiKey: 'your-api-key');
```

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

<Tip>
  Pass your own `Dio` to share an HTTP client (interceptors, logging, retry,
  test adapters) across SDKs:
</Tip>

### Identify the user

The SDK exposes a per-user scoped client. Get one with `sdk.user(appUserId)` and call methods on it.

```dart
final userClient = sdk.user('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>

<Tip>
  If your app has no sign-in flow, generate a random UUID on first launch and
  persist it with `flutter_secure_storage` (preferred) or `shared_preferences`
  as a simpler fallback.
</Tip>

When the user logs out, drop the `UserClient` reference and obtain a new one for the next user.

## Create a user

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

```dart
final user = await userClient.createOrUpdate();
```

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 throws if it's unknown, expired, ineligible, or already claimed. On success the response includes the updated `User` and any rewards granted at claim time.

```dart
try {
  final result = await userClient.claimCode('XYZ123');
  final user = result.user;
  final rewardsGranted = result.rewardsGranted;
  // Apply granted rewards in your app.
} on DioException catch (e) {
  // Handle DioException — see Error handling below.
}
```

## 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 whenever you want a fresh snapshot to unlock the matching perks in your app.

```dart
final user = await userClient.get();
// 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 `ValueNotifier`, `Riverpod` provider, or any store) 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 `createOrUpdate(...)`. All arguments are optional — pass only what you want to change.

```dart
final user = await userClient.createOrUpdate(
  isPremium: true,
  isTrial: false,
  firstSeenAt: DateTime.now(),
  metadata: {'plan': 'annual', 'platform': 'flutter'},
);
```

#### 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 `userClient.get()` or `userClient.createOrUpdate(...)`.

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

```dart
final result = await userClient.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.

```dart
await userClient.registerAppStoreTransaction(
  originalTransactionId: '1000000123456789',
);
```

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

```dart
await userClient.registerGooglePlayTransaction(
  purchaseToken: 'abc...token',
);
```

### Refresh

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

```dart
final user = await userClient.get();
```

## Error handling

The SDK delegates to Dio for transport, so HTTP errors and network failures throw `DioException`. Catch it and inspect `response.statusCode` and `response.data` to branch on API errors versus connectivity problems.

```dart
try {
  final result = await userClient.claimCode('XYZ123');
  // Handle success.
} on DioException catch (e) {
  if (e.response != null) {
    // Server responded with a non-2xx — e.response.statusCode and
    // e.response.data hold the parsed ErrorsResponse body.
  } else {
    // Network I/O, timeout, cancellation, or other transport failure.
  }
}
```

The parsed body for non-2xx responses follows the standard `ErrorsResponse` shape — a list of `ErrorObject` entries.

See [Errors](/developers/api/errors/) for the full list of error codes the API can return.
