Events
Listen to SDK lifecycle events and register a global delegate.
Beta
The Web SDK is in beta and its API may change between releases.
Listening
sw.events is a native EventTarget with typed overloads, including AbortSignal for cleanup.
const ac = new AbortController();
sw.events.addEventListener(
"subscriptionStatus_didChange",
(event) => console.log(event.detail),
{ signal: ac.signal },
);
// Removes every listener registered with this signal.
ac.abort();Handlers receive a typed CustomEvent. The payload is on event.detail.
In React, use useSuperwallEvent, which ties the subscription to the
component lifecycle.
What is emitted
The event map covers the same lifecycle the mobile SDKs report:
| Area | Events |
|---|---|
| Session | first_seen, app_open, app_close, app_launch, app_install, session_start, reset |
| Configuration | config_refresh, config_fail, config_attributes |
| Identity and attributes | identity_alias, user_attributes, device_attributes, integration_attributes |
| Subscription | subscriptionStatus_didChange, customerInfo_didChange |
| Placements | trigger_fire, paywallPresentationRequest, confirm_all_assignments |
| Paywall lifecycle | paywall_open, paywall_close, paywall_page_view, paywall_decline |
| Transactions | transaction_start, transaction_complete, transaction_fail, transaction_abandon, transaction_timeout, transaction_restore, subscription_start, freeTrial_start, nonRecurringProduct_purchase |
| Restore | restore_start, restore_complete, restore_fail |
| Discounts | discount_redeem_complete, discount_redeem_fail |
| Checkout and redemption | checkoutCompleted, redemptionCodesReceived (local-only) |
| Paywall loading | paywallPreload_start, paywallPreload_complete, paywallResponseLoad_start, paywallResponseLoad_complete, paywallResponseLoad_notFound, paywallResponseLoad_fail, paywallProductsLoad_start, paywallProductsLoad_complete, paywallProductsLoad_fail |
| Webview | paywallWebviewLoad_start, paywallWebviewLoad_complete, paywallWebviewLoad_fail, paywallWebviewLoad_timeout |
| Surveys | survey_response, survey_close |
| Custom | custom_placement, customPaywallAction |
| Other | deepLink_open, page_view, enrichment_start, enrichment_complete, enrichment_fail |
Some events are local-only and never reach Superwall's servers. Those live in LocalSuperwallEventMap. AllSuperwallEvents is the union of both.
Global delegate
The delegate receives global callbacks from the SDK. Register one at creation, or swap it later.
const sw = createSuperwall({
apiKey: "pk_…",
delegate: {
onSubscriptionStatusChange: (from, to) => {
console.log("subscription", from.status, "->", to.status);
},
},
});
sw.setDelegate(otherDelegate);
sw.setDelegate(null);The delegate covers subscription, customer, and user attribute changes, the paywall present and dismiss pairs, URL and deep-link interception, custom paywall actions, completed checkouts, code redemption, a catch-all onEvent, and onLog.
Per-call handlers passed to register fire alongside the delegate. The two are separate surfaces covering different callbacks. onSkip exists only on a per-call handler, and the delegate's methods cannot be supplied per call. Neither overrides the other, and there is no fallback between them.
Custom actions reach you two ways. onCustomPaywallAction(name) on the delegate receives the
legacy custom action from the paywall. Actions defined as custom placements arrive as the
custom_placement event, whose detail carries placementName, paywall_info, and params.
Checkout and redemption
Two local-only events report completed web checkouts. Neither changes what the SDK does next. To change that, use handler.onPurchase.
| Event | Delegate method | Fires when |
|---|---|---|
checkoutCompleted | onCheckoutCompleted(checkout, info) | Any web checkout completes. Fires before the default handling or your onPurchase runs. The detail carries the full checkout payload and paywallInfo. |
redemptionCodesReceived | onRedemptionCodesReceived(codes, info) | A completed checkout carried redemption codes. codes is a list of { code, claimed }. The event detail also carries the checkout's claimed, productId, checkoutContextId, and paywallInfo. |
sw.setDelegate({
onCheckoutCompleted: (checkout, info) =>
console.log("checkout completed", info.identifier, checkout.checkoutContextId),
});
sw.events.addEventListener("redemptionCodesReceived", (e) => {
const unclaimed = e.detail.codes.filter((c) => !c.claimed).map((c) => c.code);
});onWillRedeemLink and onDidRedeemLink fire around every redemption the SDK performs, whether it comes from sw.redeem() or from a ?code= link the default controller redeems at startup.
checkoutCompleted requires Web SDK 0.3.0 or later. redemptionCodesReceived requires 0.3.1 or later.
Forwarding to your analytics
The delegate has a catch-all for this. It receives every wire-bound event, fully typed. It also receives your own sw.track() calls, though their names are not part of the typed event map. It skips local-only events such as checkoutCompleted and redemptionCodesReceived, which have their own delegate methods.
const sw = createSuperwall({
apiKey: "pk_…",
delegate: {
onEvent: (name, detail) => analytics.track(name, detail),
},
});To subscribe to a specific set instead, type the names so detail narrows:
import type { AllSuperwallEvents } from "@superwall/paywalls-js";
const forward = (type: keyof AllSuperwallEvents) =>
sw.events.addEventListener(type, (e) => analytics.track(type, e.detail));
["paywall_open", "paywall_close", "subscriptionStatus_didChange"].forEach(forward);How is this guide?