Overview
Helium emits paywall and lifecycle events related to paywalls, purchasing, configuration, and experimentation.
This page acts as a reference to what events are available in Helium, and when each one is fired. Visit each SDK’s quickstart page to see how to listen for and handle events.
Event Types
iOS
Android
React Native
Flutter
// Base protocol for all events
protocol HeliumEvent {
var eventName: String { get }
var timestamp: Date { get }
func toDictionary() -> [String: Any]
}
// Events with paywall context
protocol PaywallContextEvent: HeliumEvent {
var triggerName: String { get }
var paywallName: String { get }
var isSecondTry: Bool { get }
}
// Product-related events
protocol ProductEvent: PaywallContextEvent {
var productId: String { get }
}
// All Event Types:
// Lifecycle: PaywallOpenEvent, PaywallCloseEvent, PaywallDismissedEvent,
// PaywallSkippedEvent, PaywallButtonPressedEvent
// Purchase: ProductSelectedEvent, PurchasePressedEvent, PurchaseSucceededEvent,
// PurchaseCancelledEvent, PurchaseFailedEvent, PurchaseRestoredEvent,
// PurchaseRestoreFailedEvent, PurchasePendingEvent
// System: InitializeStartEvent, PaywallsDownloadSuccessEvent,
// PaywallsDownloadErrorEvent, PaywallWebViewRenderedEvent
// Experiment: UserAllocatedEvent
Available Events
Paywall and Purchase Events
| Event Name | Description | Parameters |
|---|
| paywallOpen | Fires when a paywall is displayed | triggerName (String) - Associated trigger
paywallName (String) - Paywall name
isSecondTry (Boolean) - True if paywall is “second try” flow
viewType (String) - presented, triggered, or embedded
loadTimeTakenMS (Number, optional) - Loading time in milliseconds
loadingBudgetMS (Number, optional) - Loading budget in milliseconds
timestamp (Number) - When event occurred |
| paywallClose | Fires when a paywall is closed (removed from view hierarchy). Fires on both explicit dismissal AND purchase success | triggerName (String) - Associated trigger
paywallName (String) - Paywall name
isSecondTry (Boolean) - True if paywall is “second try” flow
timestamp (Number) - When event occurred |
| paywallDismissed | Fires when paywall is explicitly dismissed by user | triggerName (String) - Associated trigger
paywallName (String) - Paywall name
isSecondTry (Boolean) - True if paywall is “second try” flow
dismissAll (Boolean) - Whether entire paywall stack dismissed
timestamp (Number) - When event occurred |
| paywallSkipped | Fires if paywall display is skipped due to targeting or workflow configuration | triggerName (String) - Associated trigger
timestamp (Number) - When event occurred |
| paywallButtonPressed | Fires when a non-purchase button is pressed | buttonName (String) - Button identifier
triggerName (String) - Associated trigger
paywallName (String) - Paywall name
isSecondTry (Boolean) - True if paywall is “second try” flow
timestamp (Number) - When event occurred |
| paywallWebViewRendered | Fires when the paywall content has finished rendering | triggerName (String) - Associated trigger
paywallName (String) - Paywall name
isSecondTry (Boolean) - True if paywall is “second try” flow
webviewRenderTimeTakenMS (Number, optional) - Time to render in milliseconds
timestamp (Number) - When event occurred |
| productSelected | Fires when a user selects a product on the paywall | productId (String) - Product identifier
triggerName (String) - Associated trigger
paywallName (String) - Paywall name
isSecondTry (Boolean) - True if paywall is “second try” flow
timestamp (Number) - When event occurred |
| purchasePressed | Fires when a purchase button is pressed in the paywall | productId (String) - Product being purchased
triggerName (String) - Associated trigger
paywallName (String) - Paywall name
isSecondTry (Boolean) - True if paywall is “second try” flow
timestamp (Number) - When event occurred |
| purchaseSucceeded | Fires when a purchase is successfully completed. | productId (String) - Product identifier
triggerName (String) - Associated trigger
paywallName (String) - Paywall name
isSecondTry (Boolean) - True if paywall is “second try” flow
storeKitTransactionId (String, optional) - Transaction ID
storeKitOriginalTransactionId (String, optional) - Original transaction ID
skPostPurchaseTxnTimeMS (Number, optional) - Post-purchase transaction time
timestamp (Number) - When event occurred |
| purchaseCancelled | Fires when the purchase process is cancelled by the user | productId (String) - Product that was cancelled
triggerName (String) - Associated trigger
paywallName (String) - Paywall name
isSecondTry (Boolean) - True if paywall is “second try” flow
timestamp (Number) - When event occurred |
| purchaseFailed | Fires when the purchase fails for any reason | productId (String) - Product that failed
triggerName (String) - Associated trigger
paywallName (String) - Paywall name
isSecondTry (Boolean) - True if paywall is “second try” flow
error (String, optional) - Error message
timestamp (Number) - When event occurred |
| purchaseRestored | Fires when a previous purchase is successfully restored | productId (String) - Restored product ID
triggerName (String) - Associated trigger
paywallName (String) - Paywall name
isSecondTry (Boolean) - True if paywall is “second try” flow
timestamp (Number) - When event occurred |
| purchaseRestoreFailed | Fires when an attempt to restore purchases fails | triggerName (String) - Associated trigger
paywallName (String) - Paywall name
isSecondTry (Boolean) - True if paywall is “second try” flow
timestamp (Number) - When event occurred |
| purchasePending | Fires when purchase is in a pending state (e.g. waiting for parental approval) | productId (String) - Pending product
triggerName (String) - Associated trigger
paywallName (String) - Paywall name
isSecondTry (Boolean) - True if paywall is “second try” flow
timestamp (Number) - When event occurred |
System Events
| Event Name | Description | Parameters |
|---|
| initializeStart | Fires when SDK initialization is started | timestamp (Number) - When event occurred |
| paywallsDownloadSuccess | Fires when Helium paywalls downloaded and initialized successfully | downloadTimeTakenMS (Number, optional) - Config download time in milliseconds
imagesDownloadTimeTakenMS (Number, optional) - Images download time in milliseconds
fontsDownloadTimeTakenMS (Number, optional) - Fonts download time in milliseconds
bundleDownloadTimeMS (Number, optional) - Bundle download time in milliseconds
numAttempts (Number, optional) - Number of attempts made
timestamp (Number) - When event occurred |
| paywallsDownloadError | Fires when paywalls download failed | error (String) - Error description
numAttempts (Number, optional) - Number of attempts made
timestamp (Number) - When event occurred |
| paywallOpenFailed | Fires if a paywall fails to open and fallback fails to show | triggerName (String) - Associated trigger
paywallName (String) - Paywall name
isSecondTry (Boolean) - True if paywall is “second try” flow
error (String) - Error message describing failure
timestamp (Number) - When event occurred |
Experiment Events
| Event Name | Description | Parameters |
|---|
| userAllocated | Fires once per user upon the first experimental paywall open. | type (String) - “userAllocated”
experimentInfo (Object) - Complete experiment allocation data (see below)
timestamp (Number) - When allocation occurred |
Accessing ExperimentInfo from Events
All paywall events (any event that implements PaywallContextEvent) provide a getExperimentInfo() method to access experiment allocation data on-demand:func onPaywallEvent(_ event: HeliumEvent) {
// Access experiment info from any paywall event
if let openEvent = event as? PaywallOpenEvent,
let experimentInfo = openEvent.getEventExperimentInfo() {
print("Variant: \(experimentInfo.chosenVariantDetails?.allocationIndex ?? 0)")
print("Experiment: \(experimentInfo.experimentName ?? "none")")
}
// Track which variant led to purchase
if let purchaseEvent = event as? PurchaseSucceededEvent,
let experimentInfo = purchaseEvent.getEventExperimentInfo() {
analytics.track("conversion", variant: experimentInfo.chosenVariantDetails?.allocationIndex)
}
}
You can also access experiment info directly by trigger:if let experimentInfo = Helium.shared.getExperimentInfoForTrigger("onboarding") {
print("Variant: \(experimentInfo.chosenVariantDetails?.allocationIndex ?? 0)")
}
Fetch experiments for this user
You can also fetch all experiments for the current user with a few methods available on the Helium top level object. Make sure to call these methods after Helium.shared.initialize()
// Gets all experiments for which this user is eligible
func allExperiments() -> [ExperimentInfo]?
// Gets all experiments for which this user is eligible AND has already been enrolled
func enrolledExperiments() -> [ExperimentInfo]?
ExperimentInfo Structure
The experimentInfo object in the userAllocated event contains experiment allocation data. This event fires once per session when a user is assigned to an experiment variant, which happens on the first trigger the user sees.
Core Fields
| Field | Type | Description |
|---|
enrolledTrigger | String | The trigger where this user was enrolled in the experiment. |
Experiment Details
| Field | Type | Description |
|---|
experimentName | String | Name of the experiment |
experimentId | String | Unique identifier for the experiment. You can look this experiment id up in the Helium dashboard |
experimentType | String | Type of experiment (“A/B/n test”) |
startDate | String | When the experiment started (ISO8601 string) |
endDate | String | When the experiment is scheduled to end (ISO8601 string) |
Targeting Details
| Field | Type | Description |
|---|
audienceId | String | Audience ID that user matched (for lookup in Helium) |
audienceData | String | Stringified JSON of the logged targeting logic. Useful for reference, though we recommend using the audience defined in the Helium dashboard. |
Variant Details
| Field | Type | Description |
|---|
chosenVariantDetails | Object | Details about the assigned variant |
chosenVariantDetails.allocationName | String | Variant name. This will be the paywall name for paywall A/B/n tests. More names to come soon! |
chosenVariantDetails.allocationId | String | Variant UUID |
chosenVariantDetails.allocationIndex | Integer | Variant number (0-indexed) |
Hash Details
These fields provide information about how the user was deterministically assigned to a variant.
| Field | Type | Description |
|---|
hashDetails | Object | Hash bucketing information |
hashDetails.hashedUserIdBucket1To100 | Integer | User’s hash bucket (1-100) - deterministic value for consistent variant assignment |
hashDetails.hashedUserId | String | The actual user ID that was hashed (either the userId or heliumPersistentId depending on hashMethod) |
hashDetails.hashMethod | String | Hash method used: - "HASH_USER_ID": we’re hashing the user id passed into Helium.initialize() - "HASH_HELIUM_PERSISTENT_ID": We’re hashing a persistent device ID (does NOT persist across re-installs) |
Logged Events
All Helium events (see the full list above) get logged to your analytics backend automatically with automatic event forwarding, along with the parameters listed there. Each event’s parameters will usually show up as event properties.
Event names and properties might show up as underscored/camelcase/JSON, depending on the platform.
In addition, all lifecycle, paywall, downloadSuccess, and experimental events will include contextual and custom traits as event parameters:
Contextual Traits
Helium automatically and securely collects device and software attributes:
- Locale: country, currency, currency symbol, language, preferred languages, time zone, decimal separator, uses metric system
- Screen: brightness, bounds, native bounds, scale, native scale, dark mode enabled
- Device: device identifier, orientation, system name, system version, device model, interface idiom, storage capacity
- Application: version, build number, app name, Helium SDK version, environment
Custom Traits
Custom user traits included in the Helium.initialize() call also get sent as part of all events. If you pass custom user traits as part of a paywall presentation method call, these will override any custom traits of the same name passed in during initialize().