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 | 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 on 'list' placements | None |
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. SoredemptionMode is
set per placement, with configure()
providing the app-wide default:
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.
'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.
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; triggerredeem()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 everythingredeem() 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.
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() 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: inredeem() 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()resolvesnone. - 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’sredemptionModeoption always wins over it for that presentation.
Related
- Reduce Paywall Churn: the canonical deferred flow, end to end.
- Post-Purchase Placements: the surface that pairs with
'immediate'. - Display Resolution: the values the deferred screens render, and what happens when they don’t resolve.
show()·redeem()·placement()·configure()