> ## Documentation Index
> Fetch the complete documentation index at: https://docs.encorekit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Reward for Trying

> Promise your own prize for trying a premium brand offer, and grant it when the user says they finished.

Reward for trying lets your app promise its own prize (100 coins, a free week of Premium) for trying one premium brand offer (a real perk a brand funds to acquire customers). You describe the prize on the placement. The sheet can show the promise up front, and when the user comes back from the offer it asks whether they finished. If they say yes, Encore's server decides whether that earns the prize. When it does, the claim on the result carries `userConfirmedCompletion = true`, and your app grants the prize.

<Note>
  Requires **iOS SDK 2.3.0** or **Android SDK 2.3.0**. React Native and Flutter support ships in their own releases; check each bridge's changelog.
</Note>

## Identify your users

Encore keeps score by the user ID your app passes to `identify()`, not by the device, so only identified users are promised a prize. Call it after sign in, before `show()`:

<CodeGroup>
  ```swift iOS theme={null}
  Encore.shared.identify(userId: user.id)
  ```

  ```kotlin Android theme={null}
  Encore.shared.identify(userId = user.id)
  ```
</CodeGroup>

Anonymous users still see the question, with no prize promised, and their flag stays false. A Yes they give before signing in carries over when `identify()` links them to an account, so signing in can't earn that offer's prize again.

## What the flag means

`userConfirmedCompletion` means the user said they finished and Encore's server granted the prize. It is still the **user's own word**. It does not mean the advertiser confirmed a conversion. Encore records the answer for that user, but books no transaction, conversion, payout, or entitlement for it, and sends nothing to your server. Of the prize itself, only its `id` leaves the device; the title, detail, and icon stay on it.

To grant only when the advertiser verifies the completion, use the [offer\_completed webhook](/publishers/offers-api/guides/receive-completion-events) instead. It carries the same `transactionId` that your app reads from `result.claim`.

## Register the prize and grant it

Pass a `PublisherReward` on the placement, then read the flag on `result.claim`, the result `show()` returns. Passing a prize is a commitment: grant it whenever the flag is true.

<CodeGroup>
  ```swift iOS theme={null}
  import Encore
  import Foundation

  @MainActor
  func showLevelReward(userId: String) async {
      let result = await Encore.shared.placement("level_complete")
          .useCase(.rewardUsers)
          .publisherReward(PublisherReward(
              id: "coins_100",
              title: "100 coins",
              detail: "Added to your wallet",
              iconUrl: URL(string: "https://example.com/coin.png")
          ))
          .show()

      if let claim = result.claim, claim.userConfirmedCompletion {
          // Grant once per claim: key on the transaction, else on this user and campaign.
          grantCoins(100, dedupeKey: claim.transactionId ?? "\(userId):\(claim.campaignId)")
      }
  }

  func grantCoins(_ amount: Int, dedupeKey: String) {
      // Your wallet code goes here.
  }
  ```

  ```kotlin Android theme={null}
  import androidx.lifecycle.LifecycleCoroutineScope
  import com.encorekit.encore.Encore
  import com.encorekit.encore.UseCase
  import com.encorekit.encore.features.offers.PublisherReward
  import kotlinx.coroutines.launch

  fun showLevelReward(scope: LifecycleCoroutineScope, userId: String) {
      scope.launch {
          val result = Encore.placement("level_complete")
              .useCase(UseCase.REWARD_USERS)
              .publisherReward(
                  PublisherReward(
                      id = "coins_100",
                      title = "100 coins",
                      detail = "Added to your wallet",
                      iconUrl = "https://example.com/coin.png",
                  )
              )
              .show()

          val claim = result.claim
          if (claim != null && claim.userConfirmedCompletion) {
              // Grant once per claim: key on the transaction, else on this user and campaign.
              grantCoins(100, dedupeKey = claim.transactionId ?: "$userId:${claim.campaignId}")
          }
      }
  }

  fun grantCoins(amount: Int, dedupeKey: String) {
      // Your wallet code goes here.
  }
  ```
</CodeGroup>

<Warning>
  Grant the prize from the result `show()` returns, or passes to its callback (`show(resume:)` on iOS, `show(onResult:)` on Android), and only from there. The same result, flag included, also reaches the outcomes stream ([iOS](/publishers/ios/sdk-reference/outcomes), [Android](/publishers/android/sdk-reference/outcomes)), so an app that grants from both pays twice. Use the stream to observe, not to pay.
</Warning>

The grant key is a second guard, not the main one: the server already grants each offer's prize once per user. `transactionId` can be null, and the fallback of user ID plus `campaignId` matches that rule. Use the same user ID you gave `identify()`, so your grant key and Encore's count belong to the same person. On Android, call `showLevelReward(lifecycleScope, userId)` from an Activity or Fragment. `detail` and `iconUrl` are optional, the icon shows only for an `http` or `https` URL with a host, and a blank `id` or `title` counts as no prize, so the sheet shows no promise. The builder method is listed in the `placement()` reference for [iOS](/publishers/ios/sdk-reference/placement#reward-for-trying) and [Android](/publishers/android/sdk-reference/placement#reward-for-trying).

## How Encore decides the prize

A Yes is sent to Encore under the user's ID, and Encore's server alone decides whether it earns the prize. It grants the prize only for:

* **The first Yes for an offer:** once per user per offer, ever. This rule is fixed.
* **Within your daily limit:** at most 3 prizes per user in any 24 hours by default. The window rolls; it doesn't reset at midnight.

After any Yes, granted or not, the offer is hidden from that user's offer lists on every device, and a new claim of it is refused. The same happens once Encore verifies that the user completed an offer. A reinstall, or a second device signed in to the same account, can't earn the prize again.

The promise comes from the server too. When the sheet opens, the SDK asks whether this user can still earn a prize today, and shows the promise only when the answer is yes and the user is identified. Once the daily limit is reached, the sheet shows no promise and asks the plain question.

### Set the daily limit

You can set your own daily limit. Values are clamped to 1 through 10. Set it any time before `show()`; it survives `reset()`. The SDK sends it to the server with each presentation that passes a prize.

<CodeGroup>
  ```swift iOS theme={null}
  import Encore

  @MainActor
  func limitRewards() {
      Encore.shared.publisherRewardPolicy = PublisherRewardPolicy(dailyLimit: 1)
  }
  ```

  ```kotlin Android theme={null}
  import com.encorekit.encore.Encore
  import com.encorekit.encore.features.offers.PublisherRewardPolicy

  fun limitRewards() {
      Encore.shared.publisherRewardPolicy = PublisherRewardPolicy(dailyLimit = 1)
  }
  ```
</CodeGroup>

### No answer, no prize

If the server can't be reached when the sheet opens, there's no promise. A Yes to a promise waits up to 5 seconds for the server's answer. With no answer in that time (offline, slow, or a timeout), the flag stays false. The SDK sends the report again later, so the offer is still hidden once it lands, but a late answer never sets the flag.

### An offer the user already completed

A user's offer list can be older than their last Yes: they may have finished the offer on another device since it loaded. If they tap it, the server refuses the claim as already completed. The sheet drops that offer and the user picks another. When it was the last one, the sheet closes as flow completed (`.flowCompleted` on iOS, `FlowCompleted` on Android) with no claim on the result, so your app runs its original flow as for any presentation without a claim.

## When the flag stays false

Only a Yes the server grants sets the flag. It stays false when the user:

* answers No,
* closes the sheet without answering,
* closes the sheet while a promised Yes is still waiting for the server,
* isn't identified,
* has reached your daily limit,
* already said Yes to this offer,
* can't get an answer from the server in time,
* changes while the sheet is open, because `identify()` switched to a different user or `reset()` ran.

A Yes still waiting when the sheet closes reaches the server anyway. The server grants it and counts it toward the daily limit, but the result your app receives has the flag false.

The promise reflects the server's answer when the sheet opened. A Yes on another device in the meantime can use up the daily limit, so a promised Yes can still come back false. If your app describes the prize in its own screens, say how many a day it pays. Keep the grant idempotent on user ID plus `campaignId`, as in the sample earlier on this page, so no other path in your app pays that offer twice.

The flag never changes the claim's stage. A confirmed claim is still `claimed`, and it keeps the flag when Encore verifies the claim in strict unlock mode. Reading `result.claim` covers both stages. A Yes that earns the prize always closes the sheet, so the result you receive is the confirmed claim.

## Which users see the question

The promise and the question appear only when the served variant is built for reward for trying. Every other variant ignores the prize. Registering a prize is always safe, even for users who aren't in that variant: their flag just stays false.

## Related

* [Reward Users](/publishers/use-cases/post-action-reward): the use case this runs in.
* `ClaimedOffer` field by field: [iOS](/publishers/ios/sdk-reference/presentation-result#claimedoffer) and [Android](/publishers/android/sdk-reference/presentation-result#fact-readers).
