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

# Reduce Paywall Churn

> Recover a user who abandons your paywall: show() a partner-funded offer in deferred mode, let the SDK's primer hand off into the purchase of your exclusive offer with an extended free trial, then redeem() after that purchase to send the user to the partner.

A user opens your paywall, hesitates, and leaves without subscribing. Instead of losing
them, present a **partner-funded offer** that unlocks a better-than-standard deal on your
product: they claim the partner offer, purchase *your* exclusive offer with an extended
free trial, and are then sent to the partner. This is the canonical two-phase deferred
flow: the claim and the redirect are decoupled so your own checkout sits in between. For
the surface right after a *successful* purchase, see
[Post-Purchase Placements](/publishers/web/guides/post-purchase-placements) instead.

## The complete pattern

An example fitness app, FitTracker, whose exclusive offer is an extended 1-month free
trial (vs the standard 7 days), unlocked by claiming the partner offer:

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

Encore.configure({ apiKey: 'pk_live_yourpublishablekey' }); // deferred is the default mode
Encore.identify(userId); // bind the claim to an id that survives reloads

// 1. The user dismisses your paywall without subscribing
async function onPaywallAbandoned() {
  const result = await Encore.placement('paywall_abandon', {
    appearance: { accentColor: '#2E6BFF' },
    // display: { appDisplayName: 'FitTracker', trialPeriod: '1 month' },
    // ^ only if you don't manage the app name / entitlement / icon in the portal
  }).show();

  if (result.status !== 'claimed') {
    return; // 'dismissed' | 'unavailable': continue your normal exit path
  }

  // 2. Claim recorded, no redirect. The SDK's primer screen has handed the
  //     user off. Now sell YOUR OWN exclusive offer: the extended free trial.
  openTrialCheckout(); // your checkout: "1 month free (usually 7 days), then $9.99/mo"
}

// 3. After the user purchases your exclusive offer, from a click/flow handler:
async function onTrialCheckoutComplete() {
  const outcome = await Encore.placement('paywall_abandon').redeem();
  // outcome.status === 'redeemed': the redemption screen showed the user's
  // new plan and sent them to the partner on the CTA tap.
}
```

Full contracts: [`show()`](/publishers/web/sdk-reference/show) ·
[`redeem()`](/publishers/web/sdk-reference/redeem) ·
[`placement()`](/publishers/web/sdk-reference/placement).

## Wire up analytics

The flow above drives trials, but Encore can only credit them to the offer if your
subscription events join back to the exposures it recorded. Identify the user with your
stable customer id (as in the setup above) and forward your subscription events to Encore
with that same id. [Attribute Subscriptions to Offers](/publishers/web/guides/attribute-subscriptions)
is the how-to, including the simple path if RevenueCat already manages your subscriptions.

## Gotchas

* **Identify before `show()`.** Call `Encore.identify()` with your stable customer id before presenting the offer; that id is what links later subscription events to the offer cohort. See [Attribute Subscriptions to Offers](/publishers/web/guides/attribute-subscriptions).
* **The claim is a fact.** `show()` resolves `claimed` whether the user tapped the
  primer's CTA or dismissed it; dismissing the primer does not un-claim. Branch on the
  result, never on how the primer closed.
* **The display values must resolve.** The deferred sheet and primer render your app
  name, trial length, and logo; if they resolve from neither the portal nor `display.*`,
  `show()` resolves `dismissed` with a `CONFIGURATION_ERROR` (or `NETWORK_ERROR` when the
  config was unreachable) before any UI. See
  [when resolution fails](/publishers/web/concepts/display-resolution#when-resolution-fails).
* **Call `redeem()` from a user gesture.** The partner tab opens synchronously on the CTA
  tap; trigger `redeem()` from a click/flow handler, not a timer or background promise,
  or popup blockers may swallow it.
* **The claim survives reloads.** It is stored locally with a \~7-day TTL; even if your
  checkout navigates or reloads, `redeem()` on a fresh page still resolves it (and
  resolves `{ status: 'none' }` if nothing is pending).
* **`claimed` ≠ conversion.** Partner conversion is asynchronous and
  server-authoritative; never gate access on the client result.

## Related

* [Redemption Modes](/publishers/web/concepts/redemption-modes): the claim↔redeem coupling this flow relies on, including the primer screen.
* [Display Resolution](/publishers/web/concepts/display-resolution): where the composed copy comes from.
* [Post-Purchase Placements](/publishers/web/guides/post-purchase-placements): the immediate-mode counterpart of this guide.
* [`show()`](/publishers/web/sdk-reference/show) · [`redeem()`](/publishers/web/sdk-reference/redeem) · [`placement()`](/publishers/web/sdk-reference/placement)
