> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tryhelium.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Helium Events

> Reference for the full list of helium events

## 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

<Tabs>
  <Tab title="iOS">
    ```swift theme={null}
    // 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
    ```
  </Tab>

  <Tab title="Android">
    ```kotlin theme={null}
    // Base sealed class for all events
    sealed class HeliumEvent(
        open val timestamp: Long
    )

    // Events with paywall context
    sealed class PaywallContextEvent(
        override val timestamp: Long,
        open val triggerName: String,
        open val paywallName: String,
        open val isSecondTry: Boolean,
    ) : HeliumEvent(timestamp)

    // Product-related events
    sealed class ProductEvent(
        override val timestamp: Long,
        override val triggerName: String,
        override val paywallName: String,
        override val isSecondTry: Boolean,
        open val productId: String,
    ) : PaywallContextEvent(timestamp, triggerName, paywallName, isSecondTry)

    // All Event Classes:
    // Lifecycle: PaywallOpen, PaywallClose, PaywallDismissed, PaywallOpenFailed,
    //            PaywallSkipped, PaywallButtonPressed
    // Purchase:  ProductSelected, PurchasedPressed, PurchaseSucceeded, PurchaseCancelled,
    //            PurchaseFailed, PurchaseRestored, PurchaseRestoreFailed, PurchasePending
    // System:    InitializeStart, InitializeCalled, PaywallsDownloadSuccess,
    //            PaywallsDownloadError, PaywallWebViewRendered
    ```
  </Tab>

  <Tab title="React Native">
    ```typescript theme={null}
    export type HeliumPaywallEvent = {
      type: 'paywallOpen' | 'paywallClose' | 'paywallDismissed' |
        'paywallOpenFailed' | 'paywallSkipped' | 'paywallButtonPressed' |
        'productSelected' | 'purchasePressed' | 'purchaseSucceeded' |
        'purchaseCancelled' | 'purchaseFailed' | 'purchaseRestored' |
        'purchaseRestoreFailed' | 'purchasePending' | 'initializeStart' |
        'paywallsDownloadSuccess' | 'paywallsDownloadError' | 
        'paywallWebViewRendered' | 'userAllocated';
      triggerName?: string;
      paywallName?: string;
      productId?: string;
      buttonName?: string;
      configId?: string;
      numAttempts?: number;
      downloadTimeTakenMS?: number;
      webviewRenderTimeTakenMS?: number;
      imagesDownloadTimeTakenMS?: number;
      fontsDownloadTimeTakenMS?: number;
      bundleDownloadTimeMS?: number;
      dismissAll?: boolean;
      isSecondTry?: boolean;
      error?: string;
      experimentInfo?: ExperimentInfo;
      timestamp?: number;
    }
    ```
  </Tab>

  <Tab title="Flutter">
    ```dart theme={null}
    // Base event structure
    abstract class HeliumEvent {
      String get eventName;
      DateTime get timestamp;
      Map<String, dynamic> toMap();
    }

    // Events with paywall context
    abstract class PaywallContextEvent extends HeliumEvent {
      String get triggerName;
      String get paywallName;
      bool get isSecondTry;
    }

    // Product-related events
    abstract class ProductEvent extends PaywallContextEvent {
      String get productId;
    }

    // All Event Types:
    // Lifecycle: PaywallOpenEvent, PaywallCloseEvent, PaywallDismissedEvent,
    //            PaywallOpenFailedEvent, PaywallSkippedEvent, PaywallButtonPressedEvent
    // Purchase:  ProductSelectedEvent, PurchasePressedEvent, PurchaseSucceededEvent,
    //            PurchaseCancelledEvent, PurchaseFailedEvent, PurchaseRestoredEvent,
    //            PurchaseRestoreFailedEvent, PurchasePendingEvent
    // System:    InitializeStartEvent, PaywallsDownloadSuccessEvent,
    //            PaywallsDownloadErrorEvent, PaywallWebViewRenderedEvent
    ```
  </Tab>
</Tabs>

## Available Events

### Paywall and Purchase Events

| Event Name                 | Description                                                                                                             | Parameters                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **paywallOpen**            | Fires when a paywall is displayed                                                                                       | `triggerName` (String) - Associated trigger<br />`paywallName` (String) - Paywall name<br />`isSecondTry` (Boolean) - True if paywall is "second try" flow<br />`viewType` (String) - presented, triggered, or embedded<br />`loadTimeTakenMS` (Number, optional) - Loading time in milliseconds<br />`loadingBudgetMS` (Number, optional) - Loading budget in milliseconds<br />`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<br />`paywallName` (String) - Paywall name<br />`isSecondTry` (Boolean) - True if paywall is "second try" flow<br />`timestamp` (Number) - When event occurred                                                                                                                                                                                                                                                                                      |
| **paywallDismissed**       | Fires when paywall is explicitly dismissed by user                                                                      | `triggerName` (String) - Associated trigger<br />`paywallName` (String) - Paywall name<br />`isSecondTry` (Boolean) - True if paywall is "second try" flow<br />`dismissAll` (Boolean) - Whether entire paywall stack dismissed<br />`timestamp` (Number) - When event occurred                                                                                                                                                                                                                 |
| **paywallSkipped**         | Fires if paywall display is skipped due to targeting, workflow configuration, or existing entitlement                   | `triggerName` (String) - Associated trigger<br />`timestamp` (Number) - When event occurred                                                                                                                                                                                                                                                                                                                                                                                                     |
| **paywallButtonPressed**   | Fires when a non-purchase button is pressed                                                                             | `buttonName` (String) - Button identifier<br />`triggerName` (String) - Associated trigger<br />`paywallName` (String) - Paywall name<br />`isSecondTry` (Boolean) - True if paywall is "second try" flow<br />`timestamp` (Number) - When event occurred                                                                                                                                                                                                                                       |
| **paywallWebViewRendered** | Fires when the paywall content has finished rendering                                                                   | `triggerName` (String) - Associated trigger<br />`paywallName` (String) - Paywall name<br />`isSecondTry` (Boolean) - True if paywall is "second try" flow<br />`webviewRenderTimeTakenMS` (Number, optional) - Time to render in milliseconds<br />`timestamp` (Number) - When event occurred                                                                                                                                                                                                  |
| **productSelected**        | Fires when a user selects a product on the paywall                                                                      | `productId` (String) - Product identifier<br />`triggerName` (String) - Associated trigger<br />`paywallName` (String) - Paywall name<br />`isSecondTry` (Boolean) - True if paywall is "second try" flow<br />`timestamp` (Number) - When event occurred                                                                                                                                                                                                                                       |
| **purchasePressed**        | Fires when a purchase button is pressed in the paywall                                                                  | `productId` (String) - Product being purchased<br />`triggerName` (String) - Associated trigger<br />`paywallName` (String) - Paywall name<br />`isSecondTry` (Boolean) - True if paywall is "second try" flow<br />`timestamp` (Number) - When event occurred                                                                                                                                                                                                                                  |
| **purchaseSucceeded**      | Fires when a purchase is successfully completed.                                                                        | `productId` (String) - Product identifier<br />`triggerName` (String) - Associated trigger<br />`paywallName` (String) - Paywall name<br />`isSecondTry` (Boolean) - True if paywall is "second try" flow<br />`storeKitTransactionId` (String, optional) - Transaction ID<br />`storeKitOriginalTransactionId` (String, optional) - Original transaction ID<br />`skPostPurchaseTxnTimeMS` (Number, optional) - Post-purchase transaction time<br />`timestamp` (Number) - When event occurred |
| **purchaseCancelled**      | Fires when the purchase process is cancelled by the user                                                                | `productId` (String) - Product that was cancelled<br />`triggerName` (String) - Associated trigger<br />`paywallName` (String) - Paywall name<br />`isSecondTry` (Boolean) - True if paywall is "second try" flow<br />`timestamp` (Number) - When event occurred                                                                                                                                                                                                                               |
| **purchaseFailed**         | Fires when the purchase fails for any reason                                                                            | `productId` (String) - Product that failed<br />`triggerName` (String) - Associated trigger<br />`paywallName` (String) - Paywall name<br />`isSecondTry` (Boolean) - True if paywall is "second try" flow<br />`error` (String, optional) - Error message<br />`timestamp` (Number) - When event occurred                                                                                                                                                                                      |
| **purchaseRestored**       | Fires when a previous purchase is successfully restored                                                                 | `productId` (String) - Restored product ID<br />`triggerName` (String) - Associated trigger<br />`paywallName` (String) - Paywall name<br />`isSecondTry` (Boolean) - True if paywall is "second try" flow<br />`timestamp` (Number) - When event occurred                                                                                                                                                                                                                                      |
| **purchaseRestoreFailed**  | Fires when an attempt to restore purchases fails                                                                        | `triggerName` (String) - Associated trigger<br />`paywallName` (String) - Paywall name<br />`isSecondTry` (Boolean) - True if paywall is "second try" flow<br />`timestamp` (Number) - When event occurred                                                                                                                                                                                                                                                                                      |
| **purchasePending**        | Fires when purchase is in a pending state (e.g. waiting for parental approval)                                          | `productId` (String) - Pending product<br />`triggerName` (String) - Associated trigger<br />`paywallName` (String) - Paywall name<br />`isSecondTry` (Boolean) - True if paywall is "second try" flow<br />`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<br />`imagesDownloadTimeTakenMS` (Number, optional) - Images download time in milliseconds<br />`fontsDownloadTimeTakenMS` (Number, optional) - Fonts download time in milliseconds<br />`bundleDownloadTimeMS` (Number, optional) - Bundle download time in milliseconds<br />`numAttempts` (Number, optional) - Number of attempts made<br />`timestamp` (Number) - When event occurred |
| **paywallsDownloadError**   | Fires when paywalls download failed                                | `error` (String) - Error description<br />`numAttempts` (Number, optional) - Number of attempts made<br />`timestamp` (Number) - When event occurred                                                                                                                                                                                                                                                                                                                      |
| **paywallOpenFailed**       | Fires if a paywall fails to open and fallback fails to show        | `triggerName` (String) - Associated trigger<br />`paywallName` (String) - Paywall name<br />`isSecondTry` (Boolean) - True if paywall is "second try" flow<br />`error` (String) - Error message describing failure<br />`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"<br />`experimentInfo` (Object) - Complete experiment allocation data (see below)<br />`timestamp` (Number) - When allocation occurred |

### Accessing ExperimentInfo from Events

<Tabs>
  <Tab title="iOS">
    All paywall events (any event that implements `PaywallContextEvent`) provide a `getExperimentInfo()` method to access experiment allocation data on-demand:

    ```swift theme={null}
    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:

    ```swift theme={null}
    if let experimentInfo = Helium.shared.getExperimentInfoForTrigger("onboarding") {
        print("Variant: \(experimentInfo.chosenVariantDetails?.allocationIndex ?? 0)")
    }
    ```
  </Tab>
</Tabs>

### 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()`

```swift theme={null}
// 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:<br />- `"HASH_USER_ID"`: we're hashing the user id passed into Helium.initialize() <br />- `"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](#available-events)) 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.

<Note>
  Event names and properties might show up as underscored/camelcase/JSON, depending on the platform.
</Note>

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()`.
