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

# Redemption Modes: Deferred vs Immediate

> The redemptionMode setting is the time coupling between claiming an offer and redeeming it: 'deferred' (default) splits show() and redeem() so the host times the advertiser redirect; 'immediate' opens the advertiser tab inside the claim gesture. A per-surface property: set per placement, with configure() as the app-wide default.

A claim and its redemption are two distinct moments: the user **claims** a partner offer
in the carousel, and the SDK later **redeems** it by sending the user to the advertiser.
`redemptionMode` decides how tightly those two moments are coupled:

|                              | `'deferred'` (default)                                                                                                | `'immediate'`                                                                   |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Claim ↔ redeem coupling      | **Decoupled**: `show()` claims, the host runs its own flow, `redeem()` redirects                                      | **Coupled**: claiming redirects inside the same tap gesture                     |
| What `show()` does on claim  | Records a transaction, **no redirect**; on a `'list'` placement it then shows the [primer screen](#the-primer-screen) | Records a transaction and **opens the advertiser tab inside the claim gesture** |
| `show()` claim result        | `{ status: 'claimed' }`                                                                                               | `{ status: 'claimed' }`, identical; the tab was also opened before resolving    |
| `redeem()` needed?           | **Yes**, phase 2 of the flow                                                                                          | No, already redeemed inline                                                     |
| Who controls redirect timing | The host app                                                                                                          | The SDK                                                                         |
| Display requirements         | Requires the [host display values](/publishers/web/concepts/display-resolution) on `'list'` placements                | None                                                                            |

<Note>
  The config literal is **`'immediate'`**; `'instant'` is not a valid value.
</Note>

## Mode is a surface property

Different surfaces in the same app want different couplings: a cancel flow wants a
decoupled claim, a payment-success page wants one-tap redirect. So `redemptionMode` is
set **per placement**, with [`configure()`](/publishers/web/sdk-reference/configure)
providing the app-wide default:

```javascript theme={null}
Encore.configure({ apiKey: 'pk_live_yourpublishablekey' }); // default mode: 'deferred'

// This surface overrides the default:
await Encore.placement('post-purchase', {
  layout: 'thankYou',
  redemptionMode: 'immediate',
}).show();
```

Layout and mode are **orthogonal axes**: `layout` composes the UI, `redemptionMode`
decides the claim↔redeem coupling. Every combination is legal; see
[thankYou + deferred](#thankyou--deferred-the-legal-decoupling) for the one that earns a
console warning.

## When to use which

**Use `'deferred'` (the default)** when claiming and redirecting are separate moments,
because you run your own conversion, navigation, or server check in between. Typical cases:

* The user abandons your paywall, claims a partner-funded offer, purchases **your**
  exclusive offer with an extended free trial, and only then is sent to the partner; see
  [Reduce Paywall Churn](/publishers/web/guides/intercept-paywall-abandonment).
* You present on one page or route and redeem after a navigation or reload.
* You gate the advertiser redirect behind a server check or a real conversion.

**Use `'immediate'`** when claiming should redirect in one step: no host logic between
claim and redirect, and no pending transaction to manage. It is the recommended pairing
for post-purchase surfaces using the `'thankYou'` layout; see
[Post-Purchase Placements](/publishers/web/guides/post-purchase-placements). In immediate
mode the SDK renders nothing after a claim; the host owns the post-claim moment.

## Deferred mode: the two-phase flow

Two calls. `show()` claims; your code runs; `redeem()` redirects.

```javascript theme={null}
import Encore from '@encorekit/web-sdk';

// Phase 1: claim (no redirect). The SDK records the transaction internally;
// there is no handle to stash.
const shown = await Encore.placement('cancel_flow').show();
if (shown.status !== 'claimed') return; // 'dismissed' | 'unavailable'

// ... run your own conversion / save step ...

// Phase 2: redirect, called from a user action (button click). redeem()
// renders the redemption screen; the advertiser tab opens on the CTA tap.
const redeemed = await Encore.placement('cancel_flow').redeem();
// redeemed.status === 'redeemed'
```

**Lifecycle:** `show()` → claim → primer screen (`'list'` placements) → `claimed` →
*(host work)* → `redeem()` → redemption screen → advertiser tab opens + provisional
grant → transaction redeemed.

Two rules make the flow reliable:

* **Call `redeem()` within a user gesture's reach.** The advertiser tab is opened
  synchronously on the CTA tap to dodge popup blockers; trigger `redeem()` from a click
  handler, not a timer or background promise.
* **Identify early.** Call [`identify()`](/publishers/web/sdk-reference/identify) with your
  stable user id before presenting, so the transaction binds to an account id that survives
  reloads and navigations.

### The primer screen

On a `'list'` placement, a deferred claim does not silently close the sheet. The SDK
shows a **primer screen** in the same modal: a dual-brand lockup (your logo and the
partner's), the headline **"Amazing! Your {trial} free trial of {app} is ready"**, and an
**Activate** CTA, priming the user for the handoff into *your* activation flow. The app
name, trial length, and logo resolve through the
[display resolution chain](/publishers/web/concepts/display-resolution).

`show()` resolves `{ status: 'claimed' }` when the user taps the CTA **or dismisses the
primer in any way**: the claim is a fact, and dismissing the primer does not un-claim.
Branch your post-claim flow on the `claimed` result, never on how the primer closed.

Because the deferred `'list'` flow renders SDK-composed copy, `show()` pre-flight
requires the host display values to resolve; if they can't, it resolves
`dismissed` with a `CONFIGURATION_ERROR` or `NETWORK_ERROR` before any UI. See
[Display Resolution](/publishers/web/concepts/display-resolution#when-resolution-fails).

### The pending transaction

A deferred claim records a pending transaction **inside the SDK**: self-contained,
carrying everything [`redeem()`](/publishers/web/sdk-reference/redeem) needs to rebuild
the advertiser redirect later. No handle is returned by `show()` and none is needed;
`redeem()` resolves the pending transaction internally.

Claims are stored locally with a **\~7-day TTL** and survive page reloads, so your code never
tracks pending state. Even on a fresh page, call `redeem()` when your flow is ready (from a
click handler, e.g. your own "finish claiming your reward" button). If nothing is pending,
`redeem()` resolves `{ status: 'none' }` and renders no UI.

`placement(id).redeem()` resolves exactly that placement's transaction; it never crosses
to another placement's. The global
[`Encore.redeem()`](/publishers/web/sdk-reference/redeem) resolves the most recent pending
transaction regardless of placement.

## Immediate mode

One call. Claiming opens the advertiser tab inside the claim gesture; `show()` then resolves
`claimed` and the SDK renders nothing further.

```javascript theme={null}
// Per-placement override; or set it app-wide in configure()
const result = await Encore.placement('post-purchase', {
  redemptionMode: 'immediate',
}).show();

if (result.status === 'claimed') {
  // Advertiser tab already opened. The SDK renders nothing after the claim;
  // show your own confirmation here if you want one.
}
// No redeem() call needed in immediate mode.
```

**Lifecycle:** `show()` → claim opens the advertiser tab in the claim gesture → `claimed`
(transaction already redeemed).

## thankYou + deferred: the legal decoupling

A `'thankYou'` placement with `redemptionMode: 'deferred'` is allowed, and logs a
console warning, because it is rarely what a post-purchase surface wants. The claim is
recorded with **no redirect and no primer** (the primer is `'list'` UI): the sheet simply
closes, and you own both the post-claim moment and the later
[`redeem()`](/publishers/web/sdk-reference/redeem) call, which renders the minimal reward
story. Most post-purchase surfaces want `'immediate'` instead.

## Redirect timing and the provisional grant

Both modes record a transaction and emit the same provisional grant when the advertiser
redirect fires: in `redeem()` for deferred, inline at claim for immediate. In either
mode, `claimed`/`redeemed` are presentation facts, not conversions. Advertiser conversion
is asynchronous and server-authoritative; verify entitlements server-side, by user.

## Gotchas

* **Don't call `redeem()` on an immediate-mode claim.** The transaction is already
  redeemed inline; `redeem()` resolves `none`.
* **Expired claims resolve as `none`.** A pending transaction past its \~7-day TTL fails
  resolution; `redeem()` returns `{ status: 'none' }`, meaning "nothing to redeem".
* **Read the app-wide default** with `Encore.getConfiguration().redemptionMode`; a
  placement's `redemptionMode` option always wins over it for that presentation.

## Related

* [Reduce Paywall Churn](/publishers/web/guides/intercept-paywall-abandonment): the canonical deferred flow, end to end.
* [Post-Purchase Placements](/publishers/web/guides/post-purchase-placements): the surface that pairs with `'immediate'`.
* [Display Resolution](/publishers/web/concepts/display-resolution): the values the deferred screens render, and what happens when they don't resolve.
* [`show()`](/publishers/web/sdk-reference/show) · [`redeem()`](/publishers/web/sdk-reference/redeem) · [`placement()`](/publishers/web/sdk-reference/placement) · [`configure()`](/publishers/web/sdk-reference/configure)
