Skip to main content

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 NameDescriptionParameters
paywallOpenFires when a paywall is displayedtriggerName (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
paywallCloseFires when a paywall is closed (removed from view hierarchy). Fires on both explicit dismissal AND purchase successtriggerName (String) - Associated trigger
paywallName (String) - Paywall name
isSecondTry (Boolean) - True if paywall is “second try” flow
timestamp (Number) - When event occurred
paywallDismissedFires when paywall is explicitly dismissed by usertriggerName (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
paywallSkippedFires if paywall display is skipped due to targeting or workflow configurationtriggerName (String) - Associated trigger
timestamp (Number) - When event occurred
paywallButtonPressedFires when a non-purchase button is pressedbuttonName (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
paywallWebViewRenderedFires when the paywall content has finished renderingtriggerName (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
productSelectedFires when a user selects a product on the paywallproductId (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
purchasePressedFires when a purchase button is pressed in the paywallproductId (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
purchaseSucceededFires 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
purchaseCancelledFires when the purchase process is cancelled by the userproductId (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
purchaseFailedFires when the purchase fails for any reasonproductId (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
purchaseRestoredFires when a previous purchase is successfully restoredproductId (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
purchaseRestoreFailedFires when an attempt to restore purchases failstriggerName (String) - Associated trigger
paywallName (String) - Paywall name
isSecondTry (Boolean) - True if paywall is “second try” flow
timestamp (Number) - When event occurred
purchasePendingFires 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 NameDescriptionParameters
initializeStartFires when SDK initialization is startedtimestamp (Number) - When event occurred
paywallsDownloadSuccessFires when Helium paywalls downloaded and initialized successfullydownloadTimeTakenMS (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
paywallsDownloadErrorFires when paywalls download failederror (String) - Error description
numAttempts (Number, optional) - Number of attempts made
timestamp (Number) - When event occurred
paywallOpenFailedFires if a paywall fails to open and fallback fails to showtriggerName (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 NameDescriptionParameters
userAllocatedFires once per trigger when a user is allocated to an experiment varianttype (String) - “userAllocated”
experimentInfo (Object) - Complete experiment allocation data (see below)
timestamp (Number) - When allocation occurred

Accessing ExperimentInfo from Events

  • iOS
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.getExperimentInfo() {
        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.getExperimentInfo() {
        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)")
}

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

FieldTypeDescription
triggerStringThe trigger the user first sees that’s in this epxeriment.

Experiment Details

FieldTypeDescription
experimentNameStringName of the experiment
experimentIdStringUnique identifier for the experiment. You can look this experiment id up in the Helium dashboard
experimentTypeStringType of experiment (“A/B/n test”)
startDateStringWhen the experiment started (ISO8601 string)
endDateStringWhen the experiment is scheduled to end (ISO8601 string)

Targeting Details

FieldTypeDescription
audienceIdStringAudience ID that user matched (for lookup in Helium)
audienceDataStringStringified JSON of the logged targeting logic. Useful for reference, though we recommend using the audience defined in the Helium dashboard.

Variant Details

FieldTypeDescription
chosenVariantDetailsObjectDetails about the assigned variant
chosenVariantDetails.allocationNameStringVariant name. This will be the paywall name for paywall A/B/n tests. More names to come soon!
chosenVariantDetails.allocationIdStringVariant UUID
chosenVariantDetails.allocationIndexIntegerVariant number (1-indexed)

Hash Details

These fields provide information about how the user was deterministically assigned to a variant.
FieldTypeDescription
hashDetailsObjectHash bucketing information
hashDetails.hashedUserIdBucket1To100IntegerUser’s hash bucket (1-100) - deterministic value for consistent variant assignment
hashDetails.hashedUserIdStringThe actual user ID that was hashed (either the userId or heliumPersistentId depending on hashMethod)
hashDetails.hashMethodStringHash 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().
I