Skip to main content
Redeems a transaction that show() recorded in deferred mode. It renders the redemption screen for the pending transaction, and on the user’s CTA tap opens the advertiser in a new tab (synchronously, inside the click gesture, to dodge popup blockers) and emits the provisional grant. This is phase 2 of the two-phase flow.

When to use this

Call redeem() after a deferred show() resolved claimed and your own conversion / save step has run. Call it in response to a user gesture (e.g. a button click) so the advertiser tab opens reliably. Do not call redeem() in immediate mode — the transaction is already redeemed inline and redeem() resolves none.

Signature

function redeem(options?: RedeemOptions): Promise<RedeemResult>

RedeemOptions

interface RedeemOptions {
  placement?: string;                                      // resolve the transaction made at this placement
  host?: { name: string; status?: string; logoUrl?: string }; // optional host product card rendered as "Active"
}
FieldTypeRequiredDescription
placementstringNoResolve the transaction claimed at this placement; never crosses to another placement’s transaction.
host{ name: string; status?: string; logoUrl?: string }NoHost product card rendered as “Active” on the redemption screen.
There is no transactionId option — no transaction handle ever crosses the API. redeem() resolves the pending transaction internally:
  1. placement — the transaction claimed at that placement; never crosses to another placement (resolves none if that placement has none).
  2. Otherwise — the single most-recent pending transaction for this user.
Your code never needs to check whether anything is pending: call redeem() when your flow is ready, and it resolves { status: 'none' } if there is nothing to redeem.

RedeemResult

type RedeemResult =
  | { status: 'redeemed' }                              // advertiser redirect fired for the pending transaction
  | { status: 'dismissed'; reason?: NotGrantedReason }  // sheet closed without redeeming — see below
  | { status: 'expired' }                               // reserved — see note below
  | { status: 'none' };                                 // STRICTLY: no live pending transaction to redeem
The arms carry no transaction payload. redeem() never rejects — the error contract mirrors show(): a user-driven close resolves dismissed (no reason or a user reason), and an internal failure resolves dismissed with reason: { type: 'error', error }. On an error the pending transaction is untouched, so calling redeem() again later is safe. none always means exactly “nothing pending — don’t retry”; it is never an error in disguise.
expired is part of the type union but is not emitted by the current SDK: an expired transaction (past its ~7-day TTL) fails resolution and is returned as none. Handle none as “nothing to redeem”; treat expired as reserved for a future change.

Examples

Redeem the latest claim

import Encore from '@encorekit/web-sdk';

const result = await Encore.redeem();
if (result.status === 'redeemed') {
  // Advertiser tab opened; provisional grant sent; transaction marked redeemed.
} else if (result.status === 'none') {
  // Nothing pending — claim an offer with show() first. (Not retryable.)
} else if (result.status === 'dismissed' && result.reason?.type === 'error') {
  // Internal failure — the pending transaction is untouched; safe to retry later.
}

Two-phase flow, no handle

const shown = await Encore.show();
if (shown.status === 'claimed') {
  // ... your conversion runs ...
  await Encore.redeem(); // resolves the pending transaction internally
}
Pending transactions are stored locally with a ~7-day TTL, so the deferred flow survives a page reload: after the reload, calling redeem() still resolves the claim.

placement(id).redeem()

Redeems the transaction claimed at a named placement, equivalent to Encore.redeem({ placement: id }). It never crosses to another placement’s transaction.
// Phase 1 — claim at a named placement
await Encore.placement('cancel_flow').show();

// Phase 2 — redeem that placement's transaction
const outcome = await Encore.placement('cancel_flow').redeem();

Gotchas

  • Open the tab inside the gesture. The advertiser tab is opened synchronously on the CTA tap. Trigger redeem() from a user action, not a timer or a detached promise, or popup blockers may swallow the redirect.
  • Expired claims resolve as none. A transaction past its ~7-day TTL fails resolution and comes back as none (not expired) in the current SDK — see the note above.
  • TRANSACTION_ID substitution. If the offer’s advertiserUrl contains the literal TRANSACTION_ID, it is replaced with the transaction id when the redirect fires, and the offer’s trackingParameters are appended.
  • redeemed makes no conversion claim. It means the redirect fired — advertiser conversion happens later, asynchronously. Verify conversions and entitlements server-side, by user, via the Encore’s server-side verification — never from the client.
  • show() — phase 1 of the flow.
  • Redemption Modes — full walkthrough, the Transaction shape, and why deferred needs redeem() while immediate doesn’t.
  • placement — named placements.