Skip to main content
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 couplingDecoupled: show() claims, the host runs its own flow, redeem() redirectsCoupled: claiming redirects inside the same tap gesture
What show() does on claimRecords a transaction, no redirect; on a 'list' placement it then shows the primer screenRecords 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 flowNo, already redeemed inline
Who controls redirect timingThe host appThe SDK
Display requirementsRequires the host display values on 'list' placementsNone
The config literal is 'immediate'; 'instant' is not a valid value.

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() providing the app-wide default:
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 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.
  • 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. 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.
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() 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 free trial of 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. 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.

The pending transaction

A deferred claim records a pending transaction inside the SDK: self-contained, carrying everything 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() 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.
// 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). 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() 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.