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 |
| 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 around a dozen callbacks: subscription and customer changes, the paywall present and dismiss pairs, URL and deep-link interception, link redemption, 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.
Forwarding to your analytics
The delegate has a catch-all for this. It receives every wire-bound event, fully typed.
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?