Skip to main content

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.

Overview

Encore.placement(id).show() returns a Promise<PresentationResult>. The Web SDK does not expose global onPurchaseRequest / onPassthrough handlers — browsers handle the purchase flow directly via redirects to advertiser URLs, so there’s no purchase-delegation contract to register. Instead, you have two ways to react to a placement resolving:
  1. Async-resultawait show() and branch on the returned PresentationResult.
  2. Per-placement callbacks — chain .onGranted(...), .onNotGranted(...), .onLoadingStateChange(...) on the placement builder before calling .show().
For the cross-platform decision tree, see Integration Patterns.

Async-result pattern

The most direct shape — await the call and branch locally.
import Encore from '@encorekit/web-sdk';

async function onCancelTapped() {
  const result = await Encore.placement('cancel_flow').show();

  if (result.granted) {
    // User accepted — entitlement applied, navigate
    window.location.href = '/home';
  } else {
    // User declined or no offers — proceed with original flow
    await proceedWithCancellation(result.reason);
  }

  // Code that runs in both branches lives here, written once
  analytics.track('cancel_flow_resolved');
}

Result type

interface PresentationResult {
  granted: boolean;
  entitlement?: EntitlementType;
  reason?: NotGrantedReason;
}

Per-placement callbacks

Same shape as the async-result pattern, but expressed as chained callbacks. Useful when you want to read top-to-bottom or attach cross-cutting telemetry.
const result = await Encore.placement('cancel_flow')
  .onGranted(async (entitlement) => {
    analytics.track('offer_granted', { entitlement });
    await notifyBackend(entitlement);
  })
  .onNotGranted((reason) => {
    analytics.track('offer_declined', { reason });
  })
  .onLoadingStateChange((isLoading) => {
    setSpinner(isLoading);
  })
  .show();

// `result` is still returned — you can use both shapes together
if (result.granted) navigateHome();
onGranted / onNotGranted / onLoadingStateChange are scoped to this placement call. They aren’t global and don’t persist across calls. See placement() and presentOffer().

When to use which

  • Async-result — call site already has access to your post-grant + post-decline logic. The most common shape.
  • Per-placement callbacks — fluent style; useful when you also want to react to loading state (onLoadingStateChange) for spinners.
  • Both — you can chain callbacks and await the result. The callbacks fire first, then the promise resolves.
See the decision tree for the full rationale.

Why no global handlers?

iOS, Android, RN, and Flutter SDKs expose onPurchaseRequest because their host environments need to delegate the actual purchase to a billing library (StoreKit, Play Billing, RevenueCat, etc.). The Web has no equivalent: when a user accepts a Web offer, the SDK opens the advertiser’s URL directly. There’s nothing to delegate. If you need cross-cutting telemetry across many placements, wrap show() in a small helper:
async function showOffer(placementId) {
  analytics.track('offer_presented', { placementId });
  const result = await Encore.placement(placementId).show();
  analytics.track('offer_resolved', { placementId, granted: result.granted });
  return result;
}

Platform-specific notes

  • No Encore.shared — Web uses the default-exported Encore object directly: Encore.configure(...), Encore.placement(id).show().
  • Per-placement callbacks are scoped to the call — they don’t persist. Set them on every placement() call where you need them, or wrap in a helper.
  • Backend validation matters — entitlements granted client-side should be validated server-side. See Track Entitlements.
  • Framework integration — see the per-framework guides: React, Vue, Angular, Svelte, Next.js, Vanilla JS.

See also