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

# Android SDK

> Integrate the Kameleoon Android SDK to run experiments and activate feature flags in native Android applications.

With the Kameleoon Android SDK, you can run feature flags on native mobile Android applications.  The Android SDK is compatible with both Kotlin and Java. The SDK is easy to integrate into your applications, and its memory and network usage are low.

**Getting started**: For help getting started, see the [developer guide](#developer-guide).

**Changelog**: Latest version of the Android SDK: 4.26.1 [Changelog](https://github.com/Kameleoon/client-android/blob/master/CHANGELOG.md)

**SDK methods**: For the full reference documentation of the Android SDK methods, see the [reference](#reference) section.

## Developer guide

Follow this section to install and configure the Android SDK in your Android app and learn about advanced features.

### Getting started

Follow these steps to install and configure the Kameleoon Android SDK in your application.

#### Installation

You can install the Android SDK by adding the following dependency to the `build.gradle` file in your Android app:

```java theme={null}
dependencies {
  implementation 'com.kameleoon:kameleoon-client-android:4.20.0'
}
```

#### Additional configuration

To customize the SDK's behavior, create a `.properties` configuration file. The properties file's name and location are important:

* Create the file in your app's `assets/` directory.
* Name the file `kameleoon-client.properties`.

You can also [download a sample configuration](/assets/developer-docs/sdks/mobile-sdks/client-configs/kameleoon-client.properties) file.

These are the available properties you can set:

| Key                                                                                                                              | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | Default value        |
| -------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
| `refreshIntervalMinute` / `refresh_interval_minute` <Badge color="green" size="sm">optional</Badge>                              | Specifies the refresh interval, in minutes, for the SDK to fetch the configuration for the active experiments and feature flags. The value determines the maximum time it takes to propagate changes, such as activating or deactivating feature flags or launching experiments. If left unspecified, the default interval is 60 minutes. Additionally, a [streaming mode](/developer-docs/feature-experimentation/technical-reference/technical-considerations/#streaming-premium-option) is available that uses server-sent events (SSE) to push new configurations to the SDK automatically and apply them in real-time.                                                                                 | `60` minutes         |
| `dataExpirationIntervalMinute` / `data_expiration_interval_minute` <Badge color="green" size="sm">optional</Badge>               | Designates the predefined time period, in minutes, that the SDK stores the visitor and their associated data. Each data instance is evaluated individually, allowing you to set the amount of time the SDK saves data before automatically deleting it. If no interval is specified, the SDK does not automatically delete data from the device.                                                                                                                                                                                                                                                                                                                                                            | `Integer.MAX_VALUE`  |
| `defaultTimeoutMillisecond` / `default_timeout_millisecond` <Badge color="green" size="sm">optional</Badge>                      | Specifies the time interval, in milliseconds, that it takes for network requests from the SDK to time out. Set the value to `30000` milliseconds (30 seconds) or more if you do not have a stable connection. Some methods have additional parameters for method-specific timeouts, but if you do not specify them explicitly, the default value is used.                                                                                                                                                                                                                                                                                                                                                   | `10000` milliseconds |
| `trackingIntervalMillisecond` / `tracking_interval_millisecond` <Badge color="green" size="sm">optional</Badge>                  | Specifies the interval for tracking requests, in milliseconds. All visitors who were evaluated for any feature flag or had data flushed will be included in this tracking request, which is performed once per interval. The minimum value is `1000` ms and the maximum value is `5000` ms.                                                                                                                                                                                                                                                                                                                                                                                                                 | `1000` ms            |
| `environment` / `environment` <Badge color="green" size="sm">optional</Badge>                                                    | For customers using multi-environment experimentation and feature flagging, this option specifies which feature flag configuration to use. By default, each feature flag has the options `production`, `staging`, and `development`. If not specified, the default value is `production`. [More information](/user-manual/experimentation/feature-experimentation/configure-your-feature-flags/manage-environments).                                                                                                                                                                                                                                                                                        | `nil`                |
| `isUniqueIdentifier` / `is_unique_identifier` <Badge color="green" size="sm">optional</Badge>                                    | Indicates that the specified `visitorCode` is a unique identifier.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | `false`              |
| `networkDomain` / `network_domain` <Badge color="green" size="sm">optional</Badge>                                               | Custom domain used by SDKs for outgoing requests, often for proxying. Must be a valid domain (e.g., example.com or sub.example.com). Invalid formats default to Kameleoon's value.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | `nil`                |
| `defaultDataFile` / `default_datafile` <Badge color="green" size="sm">optional</Badge>                                           | The `default_datafile` feature ensures the Kameleoon SDK is always **READY** by providing a fallback configuration when no cached data file exists. Developers can preload a valid configuration by fetching it from `https://sdk-config.kameleoon.eu/v3/<sitecode>` and passing it as `default_datafile` during initialization. When a `dateModified` timestamp (in milliseconds) is provided and is newer than the cached version, the SDK will use the default datafile instead of the cached version. **If `dateModified` is omitted, the default datafile is only applied when no cached version exists**. This ensures the SDK always has a valid configuration, whether default, cached, or updated. | `nil`                |
| `activityTrackingIntervalMillisecond` / `activity_tracking_interval_millisecond` <Badge color="green" size="sm">optional</Badge> | Defines the interval at which activity events are sent. Modifying this parameter can have side effects. Check [this section](#using-activitytrackingintervalmillisecond) before using it. The minimum and default value is `15 000` ms. Setting this value to `0` disables periodic activity tracking; in this case, only a single activity event is sent at application startup.                                                                                                                                                                                                                                                                                                                           | `15 000` ms          |

<Note>
  If you specify a `visitorCode` and set the `isUniqueIdentifier` parameter to `true`, the SDK methods use the `visitorCode` value as the unique visitor identifier, which is useful for [cross-device experimentation](/developer-docs/cross-device-experimentation). The SDK links the flushed data to the visitor that is associated with the specified identifier.

  The `isUniqueIdentifier` can be useful in other edge-case scenarios, such as when you can't access the anonymous `visitorCode` that was originally assigned to the visitor, but you have access to an internal ID that is connected to the anonymous visitor through session merging.
</Note>

##### Using `activityTrackingIntervalMillisecond`

The `activityTrackingIntervalMillisecond` parameter is designed to help reduce battery consumption and network usage. However, changing its value can have **significant side effects** that you should carefully consider.

Review its impact on the following features:

1. **[Elapsed time triggers](/user-manual/assets/triggers/create-a-trigger#visiting-behavior)**
   * If the configured elapsed time is shorter than the tracking interval, the trigger will not fire as expected.

2. **[Elapsed time segments](/user-manual/assets/segments/create-a-segment#visiting-behavior)**
   * If the elapsed time is shorter than the tracking interval, users may not be included in the segment as intended.

3. **[Time spent goals](/user-manual/assets/goals/create-a-goal#time-spent)**
   * If the elapsed time is shorter than the tracking interval, the goal may never be reached.

4. **[Time elapsed since last visit in the results page](/user-manual/experiment-analytics/analyze-results/results-page-settings#filter-audience)**
   * Measurements for "time elapsed since last visit" become less precise when the elapsed time is close to or below the tracking interval.

5. **[Visits count](/user-manual/experiment-analytics/troubleshooting/data-discrepancies#how-visits-and-visitors-are-counted)**
   * A new visit is created after 30 minutes of inactivity. If the tracking interval is longer than 30 minutes, a new visit will be created at each tracking interval.

<Warning>
  Setting `activityTrackingIntervalMillisecond` to `0` disables periodic activity tracking entirely. In this configuration, only a single activity event is sent at application startup, which renders all of the features listed above unusable.
</Warning>

#### Initialize the Kameleoon Client

After installing the SDK in your application and setting up the app properties, you must create the Kameleoon Client. A Client is a singleton object that acts as a bridge between your application and the Kameleoon platform. It includes all of the methods and properties you need to run a feature flag.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    import com.kameleoon.KameleoonClient;
    import com.kameleoon.KameleoonClientConfig;
    import com.kameleoon.KameleoonClientFactory;
    import com.kameleoon.KameleoonException;

    public class MyApplication extends Application
    {
        private KameleoonClient kameleoonClient;
        @Override
        public void onCreate() {
            super.onCreate();
            try {
                KameleoonClientConfig config = new KameleoonClientConfig.Builder()
                    .refreshIntervalMinute(15) // in minutes, 1 hour by default, optional
                    .defaultTimeoutMillisecond(10_000) // in milliseconds, 10 seconds by default, optional
                    .trackingIntervalMillisecond(1000) // in milliseconds, 1000 ms by default, optional
                    .dataExpirationIntervalMinute(1440 * 365) // in minutes, infinity by default, optional
                    .environment("staging") // optional
                    .isUniqueIdentifier(false) // optional, false by default. Set to true if the visitorCode corresponds to your customer's unique userId.
                    .networkDomain("example.com") // optional
                    .defaultDataFile("{...}") // optional
                    .activityTrackingIntervalMillisecond(20_000) // optional, 15_000 milliseconds by default
                    .build();
                String siteCode = "a8st4f59bj";
                String visitorCode = "yourVisitorCode";
                kameleoonClient = KameleoonClientFactory.create(siteCode, visitorCode, config, getApplicationContext());
                // or, if you want, the visitor code will be generated automatically
                kameleoonClient = KameleoonClientFactory.create(siteCode, config, getApplicationContext());
            } catch (KameleoonException.SiteCodeIsEmpty | KameleoonException.VisitorCodeInvalid exception) {
                // Exceptions indicate that provided siteCode is empty or visitorCode is invalid
            } catch (Exception exception) {
                // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
            }
        }
        public KameleoonClient getKameleoonClient() {
            return kameleoonClient;
        }
    }
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    import com.kameleoon.KameleoonClientConfig
    import com.kameleoon.KameleoonClientFactory
    import com.kameleoon.KameleoonException

    class MyApplication : Application() {
        var kameleoonClient: KameleoonClient? = null
            private set

        override fun onCreate() {
            super.onCreate()
            try {
                val config = KameleoonClientConfig.Builder()
                    .refreshIntervalMinute(15) // in minutes, 1 hour by default, optional
                    .defaultTimeoutMillisecond(10_000) // in milliseconds, 10 seconds by default, optional
                    .trackingIntervalMillisecond(1000) // in milliseconds, 1000 ms by default, optional
                    .dataExpirationIntervalMinute(1440 * 365) // in minutes, infinity by default, optional
                    .environment("staging") // optional
                    .networkDomain("example.com") // optional
                    .defaultDataFile("{...}") // optional
                    .activityTrackingIntervalMillisecond(20_000) // optional, 15_000 milliseconds by default
                    .build()
                val siteCode = "a8st4f59bj"
                val visitorCode = "yourVisitorCode"
                val kameleoonClient = KameleoonClientFactory.create(siteCode, visitorCode, config, applicationContext)
                // or if you want that visitor code will be generated automatically
                kameleoonClient = KameleoonClientFactory.create(siteCode, config, applicationContext)
            } catch (e: KameleoonException.SiteCodeIsEmpty) {
                // Exception indicating that provided siteCode is empty
            } catch (e: KameleoonException.VisitorCodeInvalid) {
                // Exception indicating that provided visitor code is invalid
            } catch (e: Exception) {
                // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
            }
        }
    }
    ```
  </Tab>
</Tabs>

While executing, the `KameleoonClientFactory.create()` method initializes the client, but it is not immediately ready for use. This delay is because the Kameleoon Client must retrieve the current configuration of feature flags (along with their traffic repartition) from a Kameleoon remote server. This retrieval requires network access, which is not always available. Until the Kameleoon Client is fully ready, you should not attempt to run other methods in the Kameleoon Android SDK. Note that once the first configuration of the feature flags is fetched, it is then periodically refreshed, but even if the refresh fails for any reason, the Kameleoon client will continue to function using the previous configuration.

You can use the [`isReady()`](#isready) method to check if the Kameleoon client initialization is complete.

Alternatively, a **helper callback** can encapsulate the logic of feature flag triggering and variation implementation. The best approach ([`isReady()`](#isready) or **callback**) depends on preferences and the exact use case. Using [`isReady()`](#isready) is recommended when the SDK is expected to be ready for use soon. For example, `isReady()` is appropriate when running a feature flag on a dialog that users likely won't access for the first few seconds or minutes of navigating in the app. A callback is recommended when there is a high probability that the SDK is still initializing. For example, a feature flag that appears onscreen at the application launch should use a callback that makes the application wait until the SDK is ready or a specified timeout has expired.

<Note>
  It's your responsibility as the app developer to ensure the logic of your application code is correct within the context of A/B testing using Kameleoon. A good practice is to always assume that the application user can be left out of the feature flag when the Kameleoon client is not yet ready. This exclusion is easy to implement, because this corresponds to the implementation of the default or reference variation logic. The code samples in the next paragraph show examples of this approach.
</Note>

You're now ready to implement feature management and features flags. See the [Reference](#reference) section for details about additional methods.

#### Best practices for initialization and usage

* Initializing [`KameleoonClient`](#create) as a singleton as early as possible after the application starts is recommended, as initialization may take some time. Since initialization is asynchronous, it does not block or delay the application startup process.
* Before using `KameleoonClient`, verify that it is initialized by calling the [`runWhenReady`](#runwhenready) method. Otherwise, attempts to use the client before it is ready will result in errors.
* ⚠️ Most key methods may throw exceptions, so proper exception handling is required. Be sure to review the documentation for each method you use to understand its potential exceptions.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    // Initialize `KameleoonClient` on application startup and use it as a singleton later
    try {
        KameleoonClient kameleoonClient = KameleoonClientFactory.create("<siteCode>", getApplicationContext());
    } catch (KameleoonException ignored) {}

    // Example: Apply a discount percentage based on a feature flag variable's value
    void applyDiscountIfApplicable() {
        kameleoonClient.runWhenReady(1000, result -> {
            double discount = 0.0;
            try {
                if (result.getOrThrow()) {
                    Variation variation = kameleoonClient.getVariation("discount");
                    discount = (double) variation.getVariables().get("discount_value").getValue();
                }
            } catch (Exception ignored) { }

            if (discount > 0) {
                applyDiscount(discount);
            }
        });
    }
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    // Initialize `KameleoonClient` on application startup and use it as a singleton later
    try {
        KameleoonClient kameleoonClient = KameleoonClientFactory.create("<siteCode>", applicationContext)
    } catch (ignored: KameleoonException) {}

    // Example: Apply a discount percentage based on a feature flag variable's value
    fun applyDiscountIfApplicable() {
        kameleoonClient.runWhenReady(1000) { result ->
            val discount = runCatching {
                if (result.getOrThrow()) {
                    val variation = kameleoonClient.getVariation("discount")
                    variation.variables["discount_value"]?.value as? Double
                } else {
                    null
                }
            }.getOrNull() ?: 0.0

            if (discount > 0) {
                applyDiscount(discount)
            }
        }
    }
    ```
  </Tab>

  <Tab title="Kotlin (Coroutines)">
    ```kotlin theme={null}
    // Initialize `KameleoonClient` on application startup and use it as a singleton later
    try {
        val kameleoonClient = KameleoonClientFactory.create("<siteCode>", applicationContext)
    } catch (ignored: KameleoonException) {}

    // Example: Apply a discount percentage based on a feature flag variable's value
    suspend fun applyDiscountIfApplicable() {
        kameleoonClient.runWhenReady(1000).getOrNull() ?: return // Exit if initialization fails

        val discount = runCatching {
            val variation = kameleoonClient.getVariation("discount")
            variation.variables["discount_value"]?.value as? Double
        }.getOrNull() ?: 0.0

        if (discount > 0) {
            applyDiscount(discount)
        }
    }
    ```
  </Tab>
</Tabs>

#### Activating a feature flag

##### Retrieving a flag configuration

To implement a feature flag in your code, you must first create the feature flag in your Kameleoon account.

To determine the status or variation of a feature flag for a specific user, you should use the [`getVariation()`](#getvariation) or [`isFeatureActive()`](#isfeatureactive) method to retrieve the configuration based on the `featureKey`.

The `getVariation()` method handles both simple feature flags with ON/OFF states and more complex flags with multiple variations. The method retrieves the appropriate variation for the user by checking the feature rules, assigning the variation, and returning it based on the `featureKey` and `visitorCode`.

The `isFeatureActive()` method can be used if you want to retrieve the configuration of a simple feature flag that has only an ON or OFF state, as opposed to more complex feature flags with multiple variations or targeting options.

If your feature flag has associated variables (such as specific behaviors tied to each variation) `getVariation()` also enables you to access the [`Variation`](#variation) object, which provides details about the assigned variation and its associated experiment. This method checks whether the user is targeted, finds the visitor’s assigned variation, and saves it to storage. When `track=true`, the SDK will send the exposure event to the specified experiment on the next tracking request, which is automatically triggered based on the SDK’s [`tracking_interval_millisecond`](#additional-configuration). By default, this interval is set to 1000 milliseconds (1 second).

The `getVariation()` method allows you to control whether tracking is done. If `track=false`, no exposure events will be sent by the SDK. This is useful if you prefer not to track data through the SDK and instead rely on client-side tracking managed by the Kameleoon engine, for example. Additionally, setting `track=false` is helpful when using the `getVariations()` method, where you might only need the variations for all flags without triggering any tracking events. If you want to know more about how tracking works, view [this article](/developer-docs/feature-experimentation/technical-reference/faq-global#when-does-the-sdk-send-a-tracking-request-for-analytics)

##### Adding data points to target a user or filter / breakdown visits in reports

To target a user, ensure you've added relevant data points to their profile before retrieving the feature variation or checking if the flag is active. Use the [`addData()`](#adddata) method to add these data points to the user's profile.

To retrieve data points collected on other devices, use the [`getRemoteVisitorData()`](#getremotevisitordata) method. This method asynchronously fetches data from the servers. It is important to call `getRemoteVisitorData()` *before* retrieving the variation or checking if the feature flag is active, as this data might be required to assign a user to a given variation.

To learn more about available targeting conditions, see the [detailed article on the subject](/developer-docs/feature-experimentation/targeting-and-segmentation/native-segmentation).

Additionally, the data points you add to the visitor profile will be available when analyzing your experiments, allowing you to filter and break down your results by factors like device. See the complete list [here](/user-manual/experiment-analytics/analyze-results/results-page-settings#breakdown-audience).

If you need to track additional data points beyond what's automatically collected, you can use Kameleoon's [Custom Data feature](#customdata). Custom Data allows you to capture and analyze specific information relevant to your experiments. Don't forget to call the [`flush()`](#flush) method to send the collected data to Kameleoon servers for analysis.

##### Tracking goal conversions

When a user completes a desired action (such as making a purchase), it is recorded as a conversion. To track conversions, use the [`trackConversion()`](#trackconversion) method and provide the required `goalId` parameter.

The conversion tracking request will be sent along with the next scheduled tracking request, which the SDK sends at regular intervals (defined by [`tracking_interval_millisecond`](#additional-configuration)). If you prefer to send the request immediately, use the [`flush()`](#flush) method with the parameter `instant=true`.

### Cross-device experimentation

To support visitors who access an app from multiple devices, Kameleoon allows the synchronization of previously collected visitor data across each of the visitor's devices and reconciliation of their visit history across devices through cross-device experimentation. Case studies and detailed information on how Kameleoon handles data across devices are available in the [article on cross-device experimentation](/developer-docs/cross-device-experimentation).

#### Synchronizing custom data across devices

Although custom mapping synchronization is used to align visitor data across devices, it is not always necessary. Below are two scenarios where custom mapping sync is not required:

**Same user ID across devices**
If the same user ID is used consistently across all devices, synchronization is handled automatically without a custom mapping sync. It is enough to call the `getRemoteVisitorData()` method when you want to sync the data collected between multiple devices.

**Multi-server instances with consistent IDs**
In complex setups involving multiple servers (for example, distributed server instances), where the same user ID is available across servers, synchronization between servers (with `getRemoteVisitorData()`) is sufficient without additional custom mapping sync.

Customers who need additional data can refer to the [`getRemoteVisitorData()`](#getremotevisitordata) method description for further guidance. In the below code, it is assumed that the same unique identifier (in this case, the `visitorCode`, which can also be referred to as `userId`) is used consistently between the two devices for accurate data retrieval.

<Note>
  If you want to sync collected data in real time, you need to choose the scope **Visitor** for your custom data.
</Note>

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java title="Device A" theme={null}
    // In this, example Custom data with index `90` was set to "Visitor" scope in Kameleoon.
    final int VISITOR_SCOPE_CUSTOM_DATA_INDEX = 90;

    kameleoonClient.addData(new CustomData(VISITOR_SCOPE_CUSTOM_DATA_INDEX, "your data"));
    kameleoonClient.flush();
    ```

    ```java title="Device B" theme={null}
    // Before working with the data, call `getRemoteVisitorData`.
    kameleoonClient.getRemoteVisitorData(result -> {
        // After calling, the SDK on Device B will have access to CustomData of Visitor scope defined on Device A.
        // So, "your data" will be available to target and track the visitor.
    });
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin title="Device A" theme={null}
    // In this example Custom data with index `90` was set to "Visitor" scope on Kameleoon Platform.
    val VISITOR_SCOPE_CUSTOM_DATA_INDEX = 90

    kameleoonClient.addData(CustomData(VISITOR_SCOPE_CUSTOM_DATA_INDEX, "your data"))
    kameleoonClient.flush()
    ```

    ```kotlin title="Device B" theme={null}
    // Before working with the data, call the `getRemoteVisitorData` method.
    kameleoonClient.getRemoteVisitorData { result ->
        // After that the SDK on Device B will have an access to CustomData of Visitor scope defined on Device A.
        // So "your data" will be available for targeting and tracking for the visitor.
    }
    ```
  </Tab>

  <Tab title="Kotlin (Coroutines)">
    ```kotlin title="Device A" theme={null}
    // In this example Custom data with index `90` was set to "Visitor" scope on Kameleoon Platform.
    val VISITOR_SCOPE_CUSTOM_DATA_INDEX = 90

    kameleoonClient.addData(CustomData(VISITOR_SCOPE_CUSTOM_DATA_INDEX, "your data"))
    kameleoonClient.flush()
    ```

    ```kotlin title="Device B" theme={null}
    // Before working with the data, call the `getRemoteVisitorData` method.
    kameleoonClient.getRemoteVisitorData()
    // After that the SDK on Device B will have an access to CustomData of Visitor scope defined on Device A.
    // So "your data" will be available for targeting and tracking for the visitor.
    ```
  </Tab>
</Tabs>

#### Using custom data for session merging

[Cross-device experimentation](/developer-docs/cross-device-experimentation) allows for combining a visitor's history across each of their devices (history reconciliation). History reconciliation allows merging different visitor sessions into one. To reconcile visit history, use [`CustomData`](#customdata) to provide a unique identifier for the visitor. For more information, see the [dedicated documentation](/developer-docs/cross-device-experimentation/#activating-cross-device-history-reconciliation).

After cross-device reconciliation is enabled, calling [`getRemoteVisitorData()`](#getremotevisitordata) with the parameter `userId` retrieves all known data for a given user.

Sessions with the same identifier will always be shown the same variation in an experiment. In the Visitor view of your experiment's results pages, these sessions will appear as a single visitor.

The SDK configuration ensures that associated sessions always see the same variation of the experiment. However, there are some limitations regarding cross-device variation allocation. These limitations are outlined [here](/developer-docs/cross-device-experimentation#critical-points-and-practical-insights).

Follow the [activating cross-device history reconciliation](#cross-device-experimentation) guide to set up your custom data on the Kameleoon platform.

Afterwards, you can use the SDK normally. The following methods that may be helpful in the context of session merging:

* `getRemoteVisitorData()` with passed `isUniqueIdentifier=true` to [`KameleoonClientConfig`](#additional-configuration) - to retrieve data for all linked visitors.
* [`trackConversion()`](#trackconversion) or [`flush()`](#flush) with passed `isUniqueIdentifier=true` to `KameleoonClientConfig` - to track some data for specific visitor that is associated with another visitor.

<Tip>
  As the custom data you use as the identifier must be set to **Visitor scope**, you need to use [cross-device custom data synchronization](/developer-docs/cross-device-experimentation) to retrieve the identifier with the [`getRemoteVisitorData()`](#getremotevisitordata) method on each device.
</Tip>

Here's an example of how to use custom data for session merging.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    // In this example, `91` represents the Custom Data's index,
    // configured as a unique identifier in Kameleoon.
    final int MAPPING_INDEX = 91;
    final String FEATURE_KEY = "ff123";

    // 0. Initializing anonymous KameleoonClient

    // Assume `anonymousVisitorCode` is the randomly generated ID for that visitor.
    KameleoonClient anonymousKameleoonClient = KameleoonClientFactory.create(siteCode, anonymousVisitorCode, getApplicationContext());
    anonymousKameleoonClient.runWhenReady(result -> {
        // ...
    });

    // 1. Before the visitor is authenticated

    // Retrieve the variation for an unauthenticated visitor.
    Variation anonymousVariation = anonymousKameleoonClient.getVariation(FEATURE_KEY);

    // 2. After the visitor is authenticated

    // Assume `userId` is the authenticated visitor's visitor code.
    anonymousKameleoonClient.addData(new CustomData(MAPPING_INDEX, userId));
    anonymousKameleoonClient.flush(true);

    KameleoonClient userKameleoonClient = KameleoonClientFactory.create(
        siteCode, userId,
        (new KameleoonClientConfig.Builder())
            .isUniqueIdentifier(true) // Indicate that `userId` is a unique identifier
            .build(),
        getApplicationContext()
    );
    userKameleoonClient.runWhenReady(result -> {
        // ...
    });

    // 3. After the visitor has been authenticated

    // Retrieve the variation for the `userId`, which will match the anonymous visitor code's variation.
    Variation userVariation = userKameleoonClient.getVariation(FEATURE_KEY);
    boolean isSameVariation = userVariation.getKey().equals(anonymousVariation.getKey()); // true

    // The `userId` and `anonymousVisitorCode` are now linked and tracked as a single visitor.
    kameleoonClient.trackConversion(123, 10.0f);

    // Additionally, the linked visitors will share all fetched remote visitor data.
    kameleoonClient.getRemoteVisitorData(result -> {
        // ...
    });
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    // In this example, `91` represents the Custom Data's index
    // configured as a unique identifier in Kameleoon.
    val MAPPING_INDEX = 91
    val FEATURE_KEY = "ff123"

    // 0. Initializing anonymous KameleoonClient

    // Assume `anonymousVisitorCode` is the randomly generated ID for that visitor.
    val anonymousKameleoonClient = KameleoonClientFactory.create(siteCode, anonymousVisitorCode, applicationContext)
    anonymousKameleoonClient.runWhenReady { result ->
        // ...
    }

    // 1. Before the visitor is authenticated

    // Retrieve the variation for an unauthenticated visitor.
    val anonymousVariation = anonymousKameleoonClient.getVariation(FEATURE_KEY)

    // 2. After the visitor is authenticated

    // Assume `userId` is the authenticated visitor's visitor code.
    anonymousKameleoonClient.addData(CustomData(MAPPING_INDEX, userId))
    anonymousKameleoonClient.flush(true)

    val userKameleoonClient = KameleoonClientFactory.create(
        siteCode, userId,
        KameleoonClientConfig.Builder()
            .isUniqueIdentifier(true) // Indicate that `userId` is a unique identifier
            .build(),
        applicationContext
    )
    userKameleoonClient.runWhenReady { result ->
        // ...
    }

    // 3. After the visitor has been authenticated

    // Retrieve the variation for the `userId`, which will match the anonymous visitor code's variation.
    val userVariation = userKameleoonClient.getVariation(FEATURE_KEY)
    val isSameVariation = userVariation.getKey() == anonymousVariation.getKey() // true

    // The `userId` and `anonymousVisitorCode` are now linked and tracked as a single visitor.
    userKameleoonClient.trackConversion(123, 10.0f)

    // Additionally, the linked visitors will share all fetched remote visitor data.
    userKameleoonClient.getRemoteVisitorData { result ->
        // ...
    }
    ```
  </Tab>

  <Tab title="Kotlin (Coroutines)">
    ```kotlin theme={null}
    // In this example, `91` represents the Custom Data's index
    // configured as a unique identifier in Kameleoon.
    val MAPPING_INDEX = 91
    val FEATURE_KEY = "ff123"

    // 0. Initializing anonymous KameleoonClient

    // Assume `anonymousVisitorCode` is the randomly generated ID for that visitor.
    val anonymousKameleoonClient = KameleoonClientFactory.create(siteCode, anonymousVisitorCode, applicationContext)
    anonymousKameleoonClient.runWhenReady()

    // 1. Before the visitor is authenticated

    // Retrieve the variation for an unauthenticated visitor.
    val anonymousVariation = anonymousKameleoonClient.getVariation(FEATURE_KEY)

    // 2. After the visitor is authenticated

    // Assume `userId` is the authenticated visitor's visitor code.
    anonymousKameleoonClient.addData(CustomData(MAPPING_INDEX, userId))
    anonymousKameleoonClient.flush(true)

    val userKameleoonClient = KameleoonClientFactory.create(
        siteCode, userId,
        KameleoonClientConfig.Builder()
            .isUniqueIdentifier(true) // Indicate that `userId` is a unique identifier
            .build(),
        applicationContext
    )
    userKameleoonClient.runWhenReady()

    // 3. After the visitor has been authenticated

    // Retrieve the variation for the `userId`, which will match the anonymous visitor code's variation.
    val userVariation = userKameleoonClient.getVariation(FEATURE_KEY)
    val isSameVariation = userVariation.getKey() == anonymousVariation.getKey() // true

    // The `userId` and `anonymousVisitorCode` are now linked and tracked as a single visitor.
    userKameleoonClient.trackConversion(123, 10.0f)

    // Additionally, the linked visitors will share all fetched remote visitor data.
    userKameleoonClient.getRemoteVisitorData()
    ```
  </Tab>
</Tabs>

In this example, the application has a login page. Since the user ID is unknown at the moment of login, an anonymous visitor automatically generated by the SDK is used. The visitor code can be retrieved with the [`getVisitorCode()`](#getvisitorcode) method. After the user logs in, the anonymous visitor is associated with the user ID and used as a unique identifier for the visitor.

### Using a custom bucketing key

By default, Kameleoon uses a unique, anonymous visitor ID (`visitorCode`) to assign users to feature flag variations. This ID is typically generated and stored on the user's device (in a browser cookie for client-side and server-side SDKs—in persistent storage for mobile SDKs). However, in certain scenarios you may need to ensure all users of the same organization see the same variant of a feature flag.

The **Custom Bucketing Key** option allows you to override this default behavior by providing your own custom identifier for bucketing. This override ensures that Kameleoon's assignment logic uses your specified key instead of the default `visitorCode`.

#### Use cases

Using a custom bucketing key is essential for maintaining consistency and accuracy in your feature flag assignments, particularly in these situations:

* **Account-level or organizational experiments:** For B2B products or scenarios where you want to assign all users from the same organization to the same variation, you can use an identifier like an `accountId`. Custom bucketing keys are crucial for A/B testing features that impact an entire team or company.

By implementing a custom bucketing key, you ensure greater consistency and accuracy in your experiments, leading to more reliable results and a better user experience.

#### Technical details

When you configure a custom bucketing key for a feature flag, you provide Kameleoon with a specific identifier from your application's data:

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    kameleoonClient.addData(new CustomData(index, "newVisitorCode"));
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    kameleoonClient.addData(CustomData(index, "newVisitorCode"))
    ```
  </Tab>
</Tabs>

* **Providing the custom key:** You provide your custom identifier to the Kameleoon SDK using the [`addData()`](#adddata) method. In this method, you will pass your chosen custom bucketing key as a [`CustomData`](#customdata) object. Here, `newVisitorCode` refers to the identifier you wish to use for your bucketing (for example, the new `userId` or `accountId`).

<Warning>
  For the custom bucketing key to function correctly, it must also be defined and configured for the feature flag during the flag creation or editing process. Without this corresponding configuration, the SDK's bucketing will not apply your custom key. For detailed instructions on how to set this up in Kameleoon, refer to this [article](/user-manual/experimentation/feature-experimentation/create-and-manage-flags/create-a-feature-flag#Advanced_Flag_Settings).
</Warning>

* **Bucketing logic:** Once a custom bucketing key is provided through the `addData()` method, all hash calculations for assigning users to variations will use this `newVisitorCode` (your custom key) instead of the default `visitorCode`. Using the `newVisitorCode` means that the bucketing decision is tied to your custom identifier, ensuring consistent assignments across various contexts where that identifier is present.
* **Data tracking and analytics:** It's crucial to note that while the `newVisitorCode` (your custom key) is used for bucketing decisions, **all subsequent data (tracking events and conversions, for example) is sent and associated with the *original* `visitorCode`.** This separation ensures that your analytics accurately reflect individual user journeys and interactions within your experiment's broader context, even when bucketing is performed at a higher level (like an account) or across multiple devices/sessions. Your original visitor data remains intact for comprehensive reporting.

#### Technical requirements

To effectively use a custom bucketing key:

* The key must be a `String`.
* It must be unique for the entity you intend to bucket (for example, if using a `userId`, each user's ID should be unique).
* The key must be available to the SDK at the exact moment the feature flag decision is evaluated for that user or request.

### Targeting conditions

The Kameleoon SDKs support a variety of predefined targeting conditions that you can use to target users in your campaigns. For the list of conditions this SDK supports, see [use visit history to target users](/developer-docs/feature-experimentation/targeting-and-segmentation/native-segmentation).

You can also use your own [external data to target users](/developer-docs/apis/data-api-rest/tutorials/storing-and-retrieving-external-data-to-target-users).

### Error Handling

All methods of the **Kameleoon SDK** can throw **only** `KameleoonException` or its documented inherited exceptions (listed in the *Exceptions Thrown* section for each method).
These exceptions are **expected behavior** of the SDK. If you want to handle specific scenarios differently, you can catch individual inherited exceptions; otherwise, catching `KameleoonException` will handle all SDK‑related errors.

Although our **unit and integration tests** confirm that the SDK **never throws** `Exception` or `RuntimeException`, we understand that **patching SDK versions on Android can be difficult**, and unexpected issues may arise from **third‑party libraries** that could throw a `RuntimeException`. To prevent your application from crashing in such rare cases, we recommend that you also **catch `Exception` (or `RuntimeException`)** as an additional safeguard. This is strictly a precaution and **not an expected behavior of the SDK**.

For example:

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    try {
        // Calling a method of the SDK
    } catch (KameleoonException e) {
        // Handling expected exceptions
    } catch (Exception e) {
        // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
    }
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    try {
        // Calling a method of the SDK
    } catch (e: KameleoonException) {
        // Handling expected exceptions
    } catch (e: Exception) {
        // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
    }
    ```
  </Tab>
</Tabs>

### Logging

The SDK generates logs to reflect various internal processes and issues.

#### Log levels

The SDK supports configuring limiting logging by a log level.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    // The `NONE` log level does not allow logging.
    com.kameleoon.logging.KameleoonLogger.setLogLevel(com.kameleoon.logging.LogLevel.NONE);

    // The `ERROR` log level only allows logging issues that may affect the SDK's main behaviour.
    com.kameleoon.logging.KameleoonLogger.setLogLevel(com.kameleoon.logging.LogLevel.ERROR);

    // The `WARNING` log level allows logging issues which may require additional attention.
    // It extends the `ERROR` log level.
    // The `WARNING` log level is a default log level.
    com.kameleoon.logging.KameleoonLogger.setLogLevel(com.kameleoon.logging.LogLevel.WARNING);

    // The `INFO` log level allows logging general information on the SDK's internal processes.
    // It extends the `WARNING` log level.
    com.kameleoon.logging.KameleoonLogger.setLogLevel(com.kameleoon.logging.LogLevel.INFO);

    // The `DEBUG` level logs additional details about the SDK’s internal processes and extends the `INFO` level
    // with more granular diagnostic output.
    // This information is not intended for end-user interpretation but can be sent to our support team
    // to assist with internal troubleshooting.
    com.kameleoon.logging.KameleoonLogger.setLogLevel(com.kameleoon.logging.LogLevel.DEBUG);
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    // The `NONE` log level allows no logging.
    com.kameleoon.logging.KameleoonLogger.setLogLevel(com.kameleoon.logging.LogLevel.NONE)

    // The `ERROR` log level allows to log only issues that may affect the SDK's main behaviour.
    com.kameleoon.logging.KameleoonLogger.setLogLevel(com.kameleoon.logging.LogLevel.ERROR)

    // The `WARNING` log level allows to log issues which may require an attention.
    // It extends the `ERROR` log level.
    // The `WARNING` log level is a default log level.
    com.kameleoon.logging.KameleoonLogger.setLogLevel(com.kameleoon.logging.LogLevel.WARNING)

    // The `INFO` log level allows to log general information on the SDK's internal processes.
    // It extends the `WARNING` log level.
    com.kameleoon.logging.KameleoonLogger.setLogLevel(com.kameleoon.logging.LogLevel.INFO)

    // The `DEBUG` log level allows to log extra information on the SDK's internal processes.
    // It extends the `INFO` log level.
    com.kameleoon.logging.KameleoonLogger.setLogLevel(com.kameleoon.logging.LogLevel.DEBUG)
    ```
  </Tab>
</Tabs>

#### Custom handling of logs

The SDK writes its logs to the console output by default. This behaviour can be overridden.

<Note>
  Logging limiting by a log level is performed apart from the log handling logic.
</Note>

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    public class CustomLogger implements com.kameleoon.logging.Logger {
        // `log` method accepts logs from the SDK
        @Override
        public void log(com.kameleoon.logging.LogLevel level, String message) {
            // Custom log handling logic here. For example:
            switch (level) {
                case ERROR:
                    android.util.Log.e("your-log-tag", message);
                    break;
                case WARNING:
                    android.util.Log.w("your-log-tag", message);
                    break;
                case INFO:
                    android.util.Log.i("your-log-tag", message);
                    break;
                case DEBUG:
                    android.util.Log.d("your-log-tag", message);
                    break;
                default:
            }
        }
    }


    // Log level filtering is applied separately from log handling logic.
    // The custom logger will only accept logs that meet or exceed the specified log level.
    // Ensure the log level is set correctly.
    com.kameleoon.logging.KameleoonLogger.setLogLevel(com.kameleoon.logging.LogLevel.DEBUG); // Optional, defaults to `LogLevel.WARNING`.
    com.kameleoon.logging.KameleoonLogger.setLogger(new CustomLogger());
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    class CustomLogger : com.kameleoon.logging.Logger {

        override fun log(level: com.kameleoon.logging.LogLevel, message: String) {
            // Custom log handling logic here. For example:
            when (level) {
                com.kameleoon.logging.LogLevel.ERROR -> android.util.Log.e("your-log-tag", message)
                com.kameleoon.logging.LogLevel.WARNING -> android.util.Log.w("your-log-tag", message)
                com.kameleoon.logging.LogLevel.INFO -> android.util.Log.i("your-log-tag", message)
                com.kameleoon.logging.LogLevel.DEBUG -> android.util.Log.d("your-log-tag", message)
                else -> {
                    // Optional: handle default case if needed
                }
            }
        }
    }

    // Log level filtering is applied separately from log handling logic.
    // The custom logger will only accept logs that meet or exceed the specified log level.
    // Ensure the log level is set correctly.
    com.kameleoon.logging.KameleoonLogger.setLogLevel(com.kameleoon.logging.LogLevel.DEBUG) // Optional, defaults to `LogLevel.WARNING`.
    com.kameleoon.logging.KameleoonLogger.setLogger(CustomLogger())
    ```
  </Tab>
</Tabs>

### Passing the visitor code to a WebView

In some cases, you may need to pass the **visitor code** from the native application to a WebView that uses [Engine.js](/developer-docs/web-experimentation/implementation-and-deployment/standard-implementation) or the web [JavaScript](/developer-docs/sdks/web-sdks/js-sdk) or [React](/developer-docs/sdks/web-sdks/react-js-sdk) SDKs. The following example demonstrates the recommended way to achieve this:

<Tabs defaultTabIndex={0}>
  <Tab title="Kotlin">
    ```kotlin theme={null}
    class WebViewActivity : AppCompatActivity() {

        private var webView: WebView? = null

        private val DEFAULT_URL = "https://example.com"
        private val COOKIE_NAME = "kameleoonVisitorCode"
        private val COOKIE_DOMAIN = ".example.com"

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)

            webView = WebView(this).also { webView ->
                setContentView(webView)
                configureWebView(DEFAULT_URL, kameleoonClient)
                webView.loadUrl(DEFAULT_URL)
            }
        }

        private fun configureWebView(url: String, kameleoonClient: KameleoonClient) {
            CookieManager.getInstance().apply {
                setCookie(
                    url,
                    "$COOKIE_NAME=${kameleoonClient.visitorCode}; Domain=$COOKIE_DOMAIN; Path=/; Secure"
                )
                flush()
            }
        }
    }
    ```
  </Tab>

  <Tab title="Kotlin (Jetpack Compose)">
    ```kotlin theme={null}
    @Composable
    fun KameleoonCookieWebView(url: String, kameleoonClient: KameleoonClient) {
        AndroidView(
            factory = { context ->
                WebView(context).apply {
                    configureWebView(url, kameleoonClient)
                    loadUrl(url)
                }
            },
        )
    }

    private fun WebView.configureWebView(url: String, kameleoonClient: KameleoonClient) {
        val COOKIE_NAME = "kameleoonVisitorCode"
        val COOKIE_DOMAIN = ".example.com"

        CookieManager.getInstance().apply {
            setCookie(
                url,
                "$COOKIE_NAME=${kameleoonClient.visitorCode}; Domain=$COOKIE_DOMAIN; Path=/; Secure"
            )
            flush()
        }
    }
    ```
  </Tab>

  <Tab title="Java">
    ```java theme={null}
    public class WebViewActivity extends AppCompatActivity {

        private WebView webView;

        private static final String DEFAULT_URL = "https://example.com";
        private static final String COOKIE_NAME = "kameleoonVisitorCode";
        private static final String COOKIE_DOMAIN = ".example.com";

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            webView = new WebView(this);
            setContentView(webView);
            configureWebView(webView, DEFAULT_URL, kameleoonClient);
            webView.loadUrl(url);
        }

        private void configureWebView(WebView webView, String url, KameleoonClient kameleoonClient) {
            CookieManager cookieManager = CookieManager.getInstance();
            cookieManager.setCookie(
                    url,
                    COOKIE_NAME + "=" + kameleoonClient.getVisitorCode() + "; Domain=" + COOKIE_DOMAIN + "; Path=/; Secure"
            );
            cookieManager.flush();
        }
    }
    ```
  </Tab>
</Tabs>

## Reference

This is the full reference documentation for the Kameleoon Android SDK.

### Initialization

Once you have [installed the SDK](#installation) in your application, the first step is initializing Kameleoon. All of your application's interactions with the SDK, such as triggering an experiment, are accomplished using this Kameleoon client object.

#### create()

Call this method before any others to initialize the SDK. This method is in `com.kameleoon.KameleoonClientFactory`. Your app conducts all interactions with the SDK using the resulting `KameleoonClient` object that this method creates.

You can customize the SDK's behavior (for example, the environment, credentials, and so on) by providing a [configuration object](#additional-configuration). Otherwise, the SDK tries to find and use your configuration file instead.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    String siteCode = "a8st4f59bj";
    try {
        // pass client configuration and visitorCode as arguments
        KameleoonClientConfig config = new KameleoonClientConfig.Builder()
            .refreshIntervalMinute(15) // in minutes, 1 hour by default, optional
            .defaultTimeoutMillisecond(10_000) // in milliseconds, 10 seconds by default, optional
            .dataExpirationIntervalMinute(1440 * 365) // in minutes, infinity by default, optional
            .isUniqueIdentifier(false) // optional, false by default. Set to true if the visitorCode corresponds to your customer's unique userId.
            .environment("staging") // optional
            .build();
        String visitorCode = "yourVisitorCode";
        KameleoonClient kameleoonClient = KameleoonClientFactory.create(siteCode, visitorCode, config, getApplicationContext());
    } catch (KameleoonException.SiteCodeIsEmpty | KameleoonException.VisitorCodeInvalid exception) {
        // Exception indicates that the provided siteCode is empty or the visitorCode is invalid
    } catch (Exception exception) {
        // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
    }

    try {
        // generate visitorCode automatically and read client configuration from a file 'kameleoon-client.properties'
        KameleoonClient kameleoonClient = KameleoonClientFactory.create(siteCode, getApplicationContext());
    } catch (KameleoonException.SiteCodeIsEmpty | KameleoonException.VisitorCodeInvalid exception) {
        // Exception indicates that the provided siteCode is empty or the visitorCode is invalid
    } catch (Exception exception) {
        // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
    }
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    val siteCode = "a8st4f59bj"
    try {
        // pass client configuration and visitor code as arguments
        val config = KameleoonClientConfig.Builder()
            .refreshIntervalMinute(15) // in minutes, 1 hour by default, optional
            .defaultTimeoutMillisecond(10_000) // in milliseconds, 10 seconds by default, optional
            .dataExpirationIntervalMinute(1440 * 365) // in minutes, infinity by default, optional
            .environment("staging") // optional
            .build();
        val visitorCode = "yourVisitorCode"
        val kameleoonClient = KameleoonClientFactory.create(siteCode, visitorCode, config, applicationContext)
    } catch (e: KameleoonException.SiteCodeIsEmpty) {
        // Exception indicating that the provided siteCode is empty
    } catch (e: KameleoonException.VisitorCodeInvalid) {
        // Exception indicating that the provided visitorCode is invalid
    } catch (e: Exception) {
        // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
    }

    try {
        // generate visitorCode automatically and read client configuration from the 'kameleoon-client.properties' file
        val kameleoonClient = KameleoonClientFactory.create(siteCode, applicationContext)
    } catch (e: KameleoonException.SiteCodeIsEmpty) {
        // Exception indicating that the provided siteCode is empty
    } catch (e: KameleoonException.VisitorCodeInvalid) {
        // Exception indicating that the provided visitorCode is invalid
    } catch (e: Exception) {
        // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
    }
    ```
  </Tab>
</Tabs>

##### Parameters

| Name                                                             | Type                    | Description                                                                                                                                                                                                                                                 | Default |
| ---------------------------------------------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| siteCode <Badge color="red" size="sm">required</Badge>           | `String`                | A [unique key](/user-manual/faq#how-do-i-find-my-sitecode) identifying the Kameleoon project used with the SDK.                                                                                                                                             |         |
| visitorCode <Badge color="green" size="sm">optional</Badge>      | `String`                | An optional visitor identifier. If available, use your internal **user ID**; otherwise, the SDK will generate one automatically.                                                                                                                            | `nil`   |
| config <Badge color="green" size="sm">optional</Badge>           | `KameleoonClientConfig` | Optional SDK configuration. If provided, it is used instead of reading from an external [configuration file](#additional-configuration). If not provided, the SDK attempts to read the file, but if the file is missing, it falls back to default behavior. | `nil`   |
| applicationContext <Badge color="red" size="sm">required</Badge> | `Context`               | The application's [context](https://developer.android.com/reference/android/content/Context).                                                                                                                                                               |         |

##### Return value

| Type              | Description                                                                                                         |
| ----------------- | ------------------------------------------------------------------------------------------------------------------- |
| `KameleoonClient` | An instance of the `KameleoonClient` class that your app can then use to manage your experiments and feature flags. |

##### Exceptions thrown

| Type                 | Description                                                                                                         |
| -------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `VisitorCodeInvalid` | Exception indicating that the provided visitor code is not valid. It is either empty or longer than 255 characters. |
| `SiteCodeIsEmpty`    | Exception indicating that the specified site code is empty string which is invalid value.                           |

#### isReady()

For mobile SDKs, the Kameleoon Client can't initialize immediately, as it must perform a server call to retrieve the current configuration for the active feature flags. Use this method to check if the SDK is ready by calling `isReady()` before triggering any feature flags.

Alternatively, you can use a callback (see the [`runWhenReady()`](#runwhenready) method for details).

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    boolean ready = kameleoonClient.isReady();
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    val ready = kameleoonClient.isReady
    ```
  </Tab>
</Tabs>

##### Return value

| Type    | Description                                                                                                                 |
| ------- | --------------------------------------------------------------------------------------------------------------------------- |
| boolean | Boolean representing the SDK's status. `true` if client is fully initialized and `false` if it is not yet ready to be used. |

#### runWhenReady()

* 🔄 *Performs an asynchronous request (if the configuration is outdated or missing)*

For mobile SDKs, the `KameleoonClient` cannot initialize immediately, as it must perform a server call to retrieve the current configuration for all feature flags. Use the [`runWhenReady()`](#runwhenready) method to handle the time until the client is ready for use. Additionally, you can set a maximum timeout period to control how long the client will wait before it becomes ready.

If `result.getOrThrow()=true`, the `KameleoonClient` is initialized and ready, and the feature flags will be triggered with their respective variations. If the result is `false` or a timeout occurs, the initialization will not complete successfully.

The callback or the coroutine-based code should include logic to apply the reference variation, as the user will be excluded from the feature flag if a timeout occurs.

<Warning>
  Since the initial configuration may require a server call, this mechanism is asynchronous. Therefore, you should either:

  * Provide a `completion` callback as an argument to the method to ensure you are notified when the `KameleoonClient` is fully initialized and ready for use.
  * Use coroutines to handle asynchronous operations.
</Warning>

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    kameleoonClient.runWhenReady(1000, result -> {
        int recommendedProductsNumber = 5; // Default control number for recommended products
        try {
            if (result.getOrThrow()) {
                Variation variation = kameleoonClient.getVariation("featureKey");
                recommendedProductsNumber = (int) variation.getVariables().get("recommendedProductsNumber").getValue();
            }
        } catch (Exception ignored) {
            // The user will not be included in the experiment results and should see the control variation
        }

        applyVariation(recommendedProductsNumber);
    });
    ```

    ##### Parameters

    | Name                                                                | Type                                          | Description                                    | Default                                                                                              |
    | ------------------------------------------------------------------- | --------------------------------------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
    | timeoutMilliseconds <Badge color="green" size="sm">optional</Badge> | `int`                                         | Timeout for the initialization process         | [`defaultTimeoutMillisecond`](#create) or [`default_timeout_millisecond`](#additional-configuration) |
    | completion <Badge color="red" size="sm">required</Badge>            | `ResultCompletion<Boolean, TimeoutException>` | The callback that processes the received data. |                                                                                                      |
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    kameleoonClient.runWhenReady(1000) { result ->
        val recommendedProductsNumber = runCatching {
            if (result.getOrThrow()) {
                val variation = kameleoonClient.getVariation("featureKey")
                variation.variables["recommendedProductsNumber"]?.value as Int
            } else {
                null // The user will not be included in the experiment results and should see the control variation
            }
        }.getOrDefault(5)  // Default control number for recommended products

        applyVariation(recommendedProductsNumber)
    }
    ```

    ##### Parameters

    | Name                                                                | Type                                          | Description                                    | Default                                                                                              |
    | ------------------------------------------------------------------- | --------------------------------------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
    | timeoutMilliseconds <Badge color="green" size="sm">optional</Badge> | `Int`                                         | Timeout for the initialization process         | [`defaultTimeoutMillisecond`](#create) or [`default_timeout_millisecond`](#additional-configuration) |
    | completion <Badge color="red" size="sm">required</Badge>            | `ResultCompletion<Boolean, TimeoutException>` | The callback that processes the received data. |                                                                                                      |
  </Tab>

  <Tab title="Kotlin (Coroutines)">
    <Warning>
      A common mistake is using suspended functions inside `mapCatching`, `runCatching`, or a `try-catch` block without properly re-throwing `CancellationException`, which can interfere with coroutine cancellation. To ensure correct behavior, try to avoid calling suspend functions within these blocks.
    </Warning>

    ```kotlin theme={null}
    viewModelScope.launch {
        kameleoonClient.runWhenReady(1000).getOrNull() ?: return@launch
        val recommendedProductsNumber = runCatching {
            val variation = kameleoonClient.getVariation("featureKey")
            variation.variables["recommendedProductsNumber"]?.value as? Int
        }.getOrNull() ?: 5 // Default control number for recommended products

        applyVariation(recommendedProductsNumber)
    }
    ```

    ##### Parameters

    | Name                                                                | Type  | Description                            | Default                                                                                              |
    | ------------------------------------------------------------------- | ----- | -------------------------------------- | ---------------------------------------------------------------------------------------------------- |
    | timeoutMilliseconds <Badge color="green" size="sm">optional</Badge> | `Int` | Timeout for the initialization process | [`defaultTimeoutMillisecond`](#create) or [`default_timeout_millisecond`](#additional-configuration) |

    ##### Return value

    | Type           | Description                                                                               |
    | -------------- | ----------------------------------------------------------------------------------------- |
    | `Result<Unit>` | A Kotlin `Result` that contains either the success result or the exception that occurred. |
  </Tab>
</Tabs>

### Feature flags and variations

#### isFeatureActive()

* 📨 *Sends Tracking Data to Kameleoon (depending on the `track` parameter)*

<Note>
  This method was previously called `activateFeature`, which was removed in SDK version `4.0.0`.
</Note>

Call this method to activate a feature toggle. This method accepts a `featureKey` as a required argument to check if the specified feature will be active for a visitor.

If the visitor has never been associated with this feature flag, the method returns a random boolean value (`true` if the visitor should be shown this feature, otherwise `false`). If the visitor is already registered with this feature flag, this method returns the previous `featureFlag` value.

Ensure you properly set up error handling as shown in the example code to catch potential exceptions.

<Note>
  Kameleoon uses tracking to count sessions and visitors when you call certain methods, such as `isFeatureActive()`, `getVariation()` or `getVariations()`.

  Use the default `true` value for the `track` parameter when you expose visitors to a variation and need to count them. Set the `track` parameter to `false` only if you call these methods before you expose visitors.

  For example, if you call `getVariations()` to retrieve all variations before you expose visitors, set the `track` parameter to `false`. This setting prevents Kameleoon from prematurely counting a session. You can then trigger tracking later when you explicitly expose the visitor.

  Kameleoon sends tracking data every second by default. You can configure this interval up to five seconds using the tracking interval configuration option. Kameleoon groups tracking events into a single session as long as the interval between events is less than 30 minutes. If more than 30 minutes elapse between tracking events, Kameleoon counts the events as separate sessions. A visit appears in your reports 30 minutes after the last recorded event in the session.
</Note>

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    String featureKey = "new_checkout";

    boolean hasNewCheckout = false;
    try {
        hasNewCheckout = kameleoonClient.isFeatureActive(featureKey);
        // disabling tracking
        hasNewCheckout = kameleoonClient.isFeatureActive(featureKey, false);
    } catch (KameleoonException.SDKNotReady e) {
        // Exception indicating that the SDK has not completed its initialization yet.
    } catch (KameleoonException.FeatureNotFound e) {
        // SDK not initialized, or feature toggle not yet activated in Kameleoon - we consider the feature inactive
    } catch (Exception exception) {
        // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
    }
    if (hasNewCheckout)
    {
        // Implement new checkout code here
    }
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    val featureKey = "new_checkout"
    var hasNewCheckout = false

    try {
        hasNewCheckout = kameleoonClient.isFeatureActive(featureKey)
        // disabling tracking
        hasNewCheckout = kameleoonClient.isFeatureActive(featureKey, false)
    } catch (e: KameleoonException.SDKNotReady) {
        // Exception indicating that the SDK has not completed its initialization yet.
        hasNewCheckout = false
    } catch (e: KameleoonException.FeatureNotFound) {
        // SDK not initialized or feature toggle not yet activated on Kameleoon's side - we consider the feature inactive
        hasNewCheckout = false
    } catch (e: Exception) {
        // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
        hasNewCheckout = false
    }
    if (hasNewCheckout) {
        // Implement new checkout code here
    }
    ```
  </Tab>
</Tabs>

<Warning>
  The `isFeatureActive()` method evaluates the served variant, not the master flag state. If you exclude rules, the method uses the **Then, for everyone else serve** default state. If you select **Off** for this default state, the method always returns `false` even when the master feature flag is **On**.
</Warning>

##### Parameters

| Name       | Type    | Description                                                                                        |
| ---------- | ------- | -------------------------------------------------------------------------------------------------- |
| featureKey | String  | Unique key of the feature you want to expose to a user. This field is required.                    |
| track      | boolean | An optional parameter to enable or disable tracking of the feature evaluation (`true` by default). |

##### Return value

| Type    | Description                                            |
| ------- | ------------------------------------------------------ |
| Boolean | Value of the feature that is registered for a visitor. |

##### Exceptions thrown

| Type            | Description                                                                                                                                                                                                                                                                  |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SDKNotReady     | Exception indicating that the SDK has not completed its initialization.                                                                                                                                                                                                      |
| FeatureNotFound | Exception indicating that the requested feature ID was not found in the SDK's internal configuration. This exception usually means the feature flag has not been activated on the Kameleoon side (but code implementing the feature is already deployed in the application). |

#### getVariation()

* 📨 *Sends Tracking Data to Kameleoon (depending on the `track` parameter)*

Retrieves the [`Variation`](#variation) assigned to a given visitor for a specific feature flag.

This method takes a `visitorCode` and `featureKey` as mandatory arguments. The `track` argument is optional and defaults to `true`.

It returns the assigned `Variation` for the visitor. If the visitor is not associated with any feature flag rules, the method returns the default `Variation` for the given feature flag.

Ensure that proper error handling is implemented in your code to manage potential exceptions.

<Note>
  The default variation refers to the variation assigned to a visitor when they do not match any predefined delivery rules for a feature flag. In other words, it is the fallback variation applied to all users who are not targeted by specific rules. It's represented as the variation in the "Then, for everyone else..." section in a management interface.
</Note>

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    final String featureKey = "featureKey";
    Variation variation = null;

    try {
        variation = kameleoonClient.getVariation(featureKey);
        // disabling tracking
        variation = kameleoonClient.getVariation(featureKey, false);
    } catch (KameleoonException.SDKNotReady ex) {
        // Exception indicating that the SDK has not completed its initialization yet.
    } catch (KameleoonException.FeatureNotFound ex) {
        // The feature key is not in the configuration file that has been fetched by the SDK.
    } catch (KameleoonException.FeatureEnvironmentDisabled ex) {
        // The feature flag is disabled for the environment.
    }

    if (variation != null) {
        String title = (String) variation.getVariables().get("title").getValue();

        switch (variation.getKey()) {
            case "on":
                // Main variation key is selected for visitorCode
                break;
            case "alternative_variation":
                // Alternative variation key
                break;
            default:
                // Default variation key
                break;
        }
    }
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    val featureKey = "featureKey"
    var variation: Variation? = null

    try {
        variation = kameleoonClient.getVariation(featureKey)
        // disabling tracking
        variation = kameleoonClient.getVariation(featureKey, false)
    } catch (e: KameleoonException.SDKNotReady) {
        // Exception indicating that the SDK has not completed its initialization yet.
    } catch (e: KameleoonException.FeatureNotFound) {
        // The feature key is not yet in the configuration file that has been fetched by the SDK.
    } catch (e: KameleoonException.FeatureEnvironmentDisabled) {
        // The feature flag is disabled for the environment
    }

    val title = variation?.variables?.get("title")?.value as? String

    when (variation?.key) {
        "on" -> {
            // Main variation key is selected for visitorCode
        }
        "alternative_variation" -> {
            // Alternative variation key
        }
        else -> {
            // Default variation key
        }
    }
    ```
  </Tab>
</Tabs>

##### Parameters

| Name                                                        | Type      | Description                                                                    | Default |
| ----------------------------------------------------------- | --------- | ------------------------------------------------------------------------------ | ------- |
| `visitorCode` <Badge color="red" size="sm">required</Badge> | `String`  | Unique identifier of the visitor.                                              |         |
| `featureKey` <Badge color="red" size="sm">required</Badge>  | `String`  | Key of the feature you want to expose to a visitor.                            |         |
| `track` <Badge color="green" size="sm">optional</Badge>     | `boolean` | An optional parameter to enable or disable tracking of the feature evaluation. | `true`  |

##### Return value

| Type        | Description                                                                           |
| ----------- | ------------------------------------------------------------------------------------- |
| `Variation` | An assigned [`Variation`](#variation) to a given visitor for a specific feature flag. |

##### Exceptions thrown

| Type                         | Description                                                                                                                                                                                                                                                           |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `VisitorCodeInvalid`         | Exception indicating that the provided visitor code is not valid. It is either empty or longer than 255 characters.                                                                                                                                                   |
| `FeatureNotFound`            | Exception indicating that the requested feature key wasn't found in the internal configuration of the SDK. This usually means that the feature flag is not activated in the Kameleoon app (but code implementing the feature is already deployed in the application). |
| `FeatureEnvironmentDisabled` | Exception indicating that feature flag is disabled for the visitor's current environment (for example, production, staging, or development).                                                                                                                          |

#### getVariations()

* 📨 *Sends Tracking Data to Kameleoon (depending on the `track` parameter)*

Retrieves a map of [`Variation`](#variation) objects assigned to a given visitor across all feature flags.

This method iterates over all available feature flags and returns the assigned `Variation` for each flag associated with the specified visitor. It takes `onlyActive` and `track` as optional arguments.

* If `onlyActive` is set to `true`, the method `getVariations()` will return feature flags variations provided the user is not bucketed with the `off` variation.
* The `track` parameter controls whether or not the method will track the variation assignments. By default, it is set to `true`. If set to `false`, the tracking will be disabled.

The returned map consists of feature flag keys as keys and their corresponding `Variation` as values. If no variation is assigned for a feature flag, the method returns the default `Variation` for that flag.

Proper error handling should be implemented to manage potential exceptions.

<Note>
  The default variation refers to the variation assigned to a visitor when they do not match any predefined delivery rules for a feature flag. In other words, it is the fallback variation applied to all users who are not targeted by specific rules. It's represented as the variation in the "Then, for everyone else..." section in a management interface.
</Note>

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    try {
        Map<String, Variation> variations = kameleoonClient.getVariations();
        // only active variations
        Map<String, Variation> variations = kameleoonClient.getVariations(true);
        // disable tracking
        Map<String, Variation> variations = kameleoonClient.getVariations(false, false);
    } catch (KameleoonException.SDKNotReady ex) {
        // Exception indicating that the SDK has not completed its initialization yet.
    }
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    try {
        val variations = kameleoonClient.getVariations()
        // only active variations
        val variations = kameleoonClient.getVariations(true)
        // disable tracking
        val variations = kameleoonClient.getVariations(false, false)
    } catch (e: KameleoonException.SDKNotReady) {
        // Exception indicating that the SDK has not completed its initialization yet.
    }
    ```
  </Tab>
</Tabs>

##### Parameters

| Name                                                         | Type      | Description                                                                                                       | Default |
| ------------------------------------------------------------ | --------- | ----------------------------------------------------------------------------------------------------------------- | ------- |
| `onlyActive` <Badge color="green" size="sm">optional</Badge> | `boolean` | An optional parameter indicating whether to return variations for active (`true`) or all (`false`) feature flags. | `false` |
| `track` <Badge color="green" size="sm">optional</Badge>      | `boolean` | An optional parameter to enable or disable tracking of the feature evaluation.                                    | `true`  |

##### Return value

| Type                     | Description                                                                                                                         |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| `Map<String, Variation>` | Map that contains the assigned [`Variation`](#variation) objects of the feature flags using the keys of the corresponding features. |

##### Exceptions thrown

| Type          | Description                                          |
| ------------- | ---------------------------------------------------- |
| `SDKNotReady` | Indicates that the SDK is not yet fully initialized. |

#### setForcedVariation()

The method allows you to programmatically assign a specific [`Variation`](#variation) to a user, bypassing the standard evaluation process. This is especially valuable for controlled experiments where the usual evaluation logic is not required or must be skipped. It can also be helpful in scenarios like debugging or custom testing.

When a **forced** variation is set, it overrides Kameleoon's real-time evaluation logic. Processes like segmentation, targeting conditions, and algorithmic calculations are skipped. To preserve segmentation and targeting conditions during an experiment, set `forceTargeting=false` instead.

A forced variation is treated the same as an evaluated variation. It is tracked in analytics and stored in the user context like any standard evaluated variation, ensuring consistency in reporting.

The method may throw exceptions under certain conditions (e.g., invalid parameters, user context, or internal issues). Proper exception handling is essential to ensure that your application remains stable and resilient.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    final int experimentId = 9516;
    try {
        // Forcing the variation "on" for the experiment 9516 for the visitor
        kameleoonClient.setForcedVariation(experimentId, "on");

        // Forcing the variation "on" while preserving segmentation and targeting conditions during the experiment
        kameleoonClient.setForcedVariation(experimentId, "on", false);

        // Resetting the forced variation for the experiment 9516 for the visitor
        kameleoonClient.setForcedVariation(experimentId, null);
    } catch (KameleoonException e) {
        // Handling the exception
    }
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    val experimentId = 9516
    try {
        // Forcing the variation "on" for the experiment 9516 for the visitor
        kameleoonClient.setForcedVariation(experimentId, "on")

        // Forcing the variation "on" while preserving segmentation and targeting conditions during the experiment
        kameleoonClient.setForcedVariation(experimentId, "on", false)

        // Resetting the forced variation for the experiment 9516 for the visitor
        kameleoonClient.setForcedVariation(experimentId, null)
    } catch (e: KameleoonException) {
        // Handling the exception
    }
    ```
  </Tab>
</Tabs>

##### Parameters

| Name                                                             | Type      | Description                                                                                                                                                                  | Default |
| ---------------------------------------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `experimentId` <Badge color="red" size="sm">required</Badge>     | `int`     | **Experiment Id** that will be targeted and selected during the evaluation process.                                                                                          |         |
| `variationKey` <Badge color="red" size="sm">required</Badge>     | `String`  | **Variation Key** corresponding to a `Variation` that should be forced as the returned value for the experiment. If the value is `null`, the forced variation will be reset. |         |
| `forceTargeting` <Badge color="green" size="sm">optional</Badge> | `boolean` | Indicates whether targeting for the experiment should be forced and skipped (`true`) or applied as in the standard evaluation process (`false`).                             | `true`  |

##### Exceptions thrown

| Type                        | Description                                                                                                                                                                                                                                           |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SDKNotReady`               | Indicates that the SDK is not yet fully initialized.                                                                                                                                                                                                  |
| `FeatureExperimentNotFound` | Exception indicating that the requested experiment id has not been found in the SDK's internal configuration. This is usually normal and means that the rule's corresponding experiment has not yet been activated on Kameleoon's side.               |
| `FeatureVariationNotFound`  | Exception indicating that the requested variation key(id) has not been found in the internal configuration of the SDK. This is usually normal and means that the variation's corresponding experiment has not yet been activated on Kameleoon's side. |

<Info>
  In most cases, only the basic error, `KameleoonException`, needs to be handled, as demonstrated in the example. However, if different types of errors require a response, handle each one separately based on specific requirements. Additionally, for enhanced reliability, general language errors can be handled by including `Exception`.
</Info>

#### evaluateAudiences()

* 📨 *Sends Tracking Data to Kameleoon*

This method evaluates visitors against all available Audiences Explorer segments and tracks those who match.

`evaluateAudiences()` should be called **after all relevant visitor data has been set or updated**, and **just before** getting a feature variation or checking a feature flag. This approach ensures that the visitor is evaluated against the most current data available, allowing for accurate audience assignment based on all criteria.

After calling this method, you can perform a detailed analysis of segment performance in Audiences Explorer.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    try {
        kameleoonClient.evaluateAudiences();
    } catch (KameleoonException e) {
        // Handling the exception
    }
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    try {
        kameleoonClient.evaluateAudiences()
    } catch (e: KameleoonException) {
        // Handling the exception
    }
    ```
  </Tab>
</Tabs>

##### Exceptions thrown

| Type          | Description                                          |
| ------------- | ---------------------------------------------------- |
| `SDKNotReady` | Indicates that the SDK is not yet fully initialized. |

<Info>
  In most cases, only the basic error, `KameleoonException`, needs to be handled, as demonstrated in the example. However, if different types of errors require a response, handle each one separately based on specific requirements. Additionally, for enhanced reliability, general language errors can be handled by including `Exception`.
</Info>

#### getDataFile()

<Tip>
  To evaluate all feature flags, use [`getVariations()`](#getvariations). This method is more efficient than calling `DataFile` and iterating through flags with [`getVariation()`](#getvariation).
</Tip>

Returns the current SDK configuration as a [`DataFile`](#datafile) object.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    try {
        DataFile dataFile = kameleoonClient.getDataFile();
    } catch (KameleoonException.SDKNotReady e) {
        // Exception indicates that the SDK has not completed its initialization yet.
    } catch (Exception e) {
        // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
    }
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    try {
        val dataFile = kameleoonClient.dataFile
        val dateModified = dataFile.dateModified
    } catch (e: KameleoonException.SDKNotReady) {
        // Exception indicates that the SDK has not completed its initialization yet.
    } catch (e: Exception) {
        // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
    }
    ```
  </Tab>
</Tabs>

##### Return value

| Type       | Description                                                  |
| ---------- | ------------------------------------------------------------ |
| `DataFile` | The [`DataFile`](#datafile) containing the SDK configuration |

##### Errors thrown

| Type          | Description                                          |
| ------------- | ---------------------------------------------------- |
| `SDKNotReady` | Indicates that the SDK is not yet fully initialized. |

### Goals

#### trackConversion()

* 📨 *Sends Tracking Data to Kameleoon*

Use this method to track conversions. This method requires `goalId` to track conversion on this particular [goal](/user-manual/assets/goals/create-a-goal). In addition, this method also accepts `revenue`, `metadata` and `negative` arguments.

The `trackConversion()` method doesn't return any value. This method is non-blocking as the server call is made asynchronously.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    final int goalId = 83023;
    kameleoonClient.trackConversion(goalId); // default revenue

    kameleoonClient.trackConversion(goalId, 10); // provided revenue == 10

    kameleoonClient.trackConversion(goalId, new CustomData(1, "metadata")); // Add metadata
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    val goalId = 83023
    kameleoonClient.trackConversion(goalId) // default revenue

    kameleoonClient.trackConversion(goalId, 10f) // provided revenue == 10

    kameleoonClient.trackConversion(goalId, CustomData(1, "metadata")) // Add metadata
    ```
  </Tab>
</Tabs>

##### Parameters

| Name                                                       | Type            | Description                                                                                                                      | Default             |
| ---------------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| `goalId` <Badge color="red" size="sm">required</Badge>     | `int`           | ID of the goal.                                                                                                                  |                     |
| `revenue` <Badge color="green" size="sm">optional</Badge>  | `float`         | Revenue of the conversion.                                                                                                       | `0`                 |
| `negative` <Badge color="green" size="sm">optional</Badge> | `boolean`       | Defines if the revenue is positive or negative.                                                                                  | `false`             |
| `metadata` <Badge color="green" size="sm">optional</Badge> | `CustomData...` | Metadata of the conversion. [Must be defined beforehand in the Kameleoon App](/user-manual/assets/goals/create-a-goal#metadata). | `new CustomData[0]` |

<Note>
  metadata values are accessible through [raw data exports](/user-manual/experiment-analytics/analyze-results/results-page-actions#Export) and [the results page](/user-manual/experiment-analytics/analyze-results/goal-metadata).

  If the `metadata` parameter is provided, Kameleoon will use these specified values for the current conversion instead of what was previously collected using the [`addData()`](#adddata) method. If the parameter is omitted, Kameleoon will use the last tracked values for those [`CustomData`](#customdata) prior to the conversion and within the same visit.

  Kameleoon will only consider the metadata values that are explicitly passed as parameters to the `trackConversion()` method.

  In the example below, Kameleoon will associate the conversion only with the custom data value explicitly provided as a parameter (here: index 5 with the value 'Amex Credit Card').

  <Tabs defaultTabIndex={1}>
    <Tab title="Java">
      ```java theme={null}
      kameleoonClient.addData(new CustomData(5, "Credit Card"), new CustomData(9, "Express Delivery"));
      kameleoonClient.trackConversion(1000, new CustomData(5, "Amex Credit Card"));
      ```
    </Tab>

    <Tab title="Kotlin">
      ```kotlin theme={null}
      kameleoonClient.addData(CustomData(5, "Credit Card"), CustomData(9, "Express Delivery"))
      kameleoonClient.trackConversion(1000, CustomData(5, "Amex Credit Card"))
      ```
    </Tab>
  </Tabs>
</Note>

### Events

#### onUpdateConfiguration()

<Note>
  This method was previously named `updateConfigurationHandler`, which was removed in SDK version `4.0.0` release.
</Note>

The `onUpdateConfiguration()` method allows you to handle the event when configuration has updated data. It takes one input parameter, **completion**. The completion that will be called when the configuration is updated using a real-time configuration event.

<Note>
  This handler only fires when the SDK is running in [streaming mode](/developer-docs/feature-experimentation/technical-reference/technical-considerations#streaming-premium-option) (server-sent events). It is **not** called for configuration refreshes performed in the default polling mode (`refreshIntervalMinute`).
</Note>

##### Parameters

| Name         | Type                                | Description                                                                                              |
| ------------ | ----------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `completion` | `ResultCompletion<Long, Exception>` | The handler that will be called when the configuration is updated using a real-time configuration event. |

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    kameleoonClient.onUpdateConfiguration(result -> {
        if (result.isSuccess()) {
            // result value contains the value of Unix time (number of seconds that have elapsed since January 1, 1970) when configuration was updated
        }
    });
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    kameleoonClient.onUpdateConfiguration { result ->
        if (result.isSuccess) {
            // result value contains the value of Unix time (number of seconds that have elapsed since January 1, 1970) when configuration was updated
        }
    }
    ```
  </Tab>
</Tabs>

### Visitor data

#### getVisitorCode()

Returns unique visitor code used in SDK.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    String visitorCode = kameleoonClient.getVisitorCode();
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    val visitorCode = kameleoonClient.visitorCode
    ```
  </Tab>
</Tabs>

##### Return value

| Type     | Description                                            |
| -------- | ------------------------------------------------------ |
| `String` | String representing a unique visitor code used in SDK. |

#### addData()

The `addData()` method adds [targeting data](#data-types) to storage so other methods can use the data to decide whether or not to target the current visitor.

The `addData()` method does not return any value and does not interact with Kameleoon back-end servers on its own. Instead, all the declared data is saved for future transmission using the [`flush()`](#flush) method. This approach reduces the number of server calls made, as the data is typically grouped into a single server call that is triggered by the `flush()`.

The [`trackConversion()`](#trackconversion) method also sends out any previously associated data, just like the `flush()`. The same holds true for [`getVariation()`](#getvariation) and [`getVariations()`](#getvariations) methods if an experimentation rule is triggered.

<Tip>
  Each visitor can only have one instance of associated data for most data types. However, [`CustomData`](#customdata) is an exception. Visitors can have one instance of associated `CustomData` per index.
</Tip>

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    // Add a single data item (tracked by default)
    kameleoonClient.addData(new CustomData(1, "value"));

    // Add multiple data items (tracked by default)
    kameleoonClient.addData(new CustomData(1, "value"), new Geolocation("France"));

    // Add multiple data items stored locally for targeting only (not sent to the Kameleoon Data API)
    kameleoonClient.addData(false, new CustomData(1, "value"), new Geolocation("France"));
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    // Add a single data item (tracked by default)
    kameleoonClient.addData(CustomData(1, "value"))

    // Add multiple data items (tracked by default)
    kameleoonClient.addData(CustomData(1, "value"), Geolocation("France"))

    // Add multiple data items stored locally for targeting only (not sent to the Kameleoon Data API)
    kameleoonClient.addData(false, CustomData(1, "value"), Geolocation("France"))
    ```
  </Tab>
</Tabs>

##### Parameters

| Name                                                    | Type      | Description                                                                                                                                                                                  | Default value |
| ------------------------------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| `track` <Badge color="green" size="sm">optional</Badge> | `boolean` | Specifies whether the added data is eligible for tracking. When set to `false`, the data is stored locally and used only for targeting evaluation; it is not sent to the Kameleoon Data API. | `true`        |
| `data` <Badge color="red" size="sm">required</Badge>    | `Data...` | Collection of Kameleoon data types.                                                                                                                                                          |               |

#### flush()

* 📨 *Sends Tracking Data to Kameleoon*

`flush()` takes the Kameleoon data associated with a visitor, and sends a tracking request along with all of the data that were added previously using the `addData()` method that has not yet been sent when calling one of [these methods](/developer-docs/feature-experimentation/technical-reference/faq-global#when-does-the-sdk-send-a-tracking-request-for-analytics). `flush()` is non-blocking, as the server call is made asynchronously.

`flush()` provides control over when the data associated with a visitor is sent to the servers. For instance, if `addData()` is called a dozen times, sending data to the server after each `addData()` invocation would be inefficient. Call `flush()` once at the end.

The `flush()` method uses `visitorCode` as the unique visitor identifier, which is useful for [cross-device experimentation](/developer-docs/cross-device-experimentation). If you set the `isUniqueIdentifier` configuration parameter to `true`, the SDK links the flushed data to the visitor associated with the specified identifier.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    kameleoonClient.addData(Device.phone());
    kameleoonClient.addData(new Conversion(32, 10f, false));

    kameleoonClient.flush(); // Interval tracking (most performant tracking method)

    kameleoonClient.flush(true); // Instant tracking
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    kameleoonClient.addData(Device.phone())
    kameleoonClient.addData(Conversion(32, 10f, false))

    kameleoonClient.flush() // Interval tracking (most performant tracking method)

    kameleoonClient.flush(true) // Instant tracking
    ```
  </Tab>
</Tabs>

##### Parameters

| Name    | Type    | Description                                                                                                                                                                                 |
| ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| instant | boolean | Boolean flag indicating whether the data should be sent instantly (`true`) or according to the scheduled tracking interval (`false`). This field is optional. The default value is `false`. |

#### getRemoteData()

* 🔄 *Performs an asynchronous request*

<Note>
  This method was previously called `retrieveDataFromRemoteSource`, which was removed in SDK version `4.0.0`.
</Note>

Use this method to retrieve data from a remote Kameleoon server based on the active `siteCode` and the `key` argument (or the active `visitorCode` if the `key` is omitted). The `visitorCode` and `siteCode` are specified in `KameleoonClientFactory.create()`. Data can be stored quickly and conveniently on highly scalable remote servers using the Kameleoon Data API. The application can then retrieve the data using this method.

<Warning>
  Since a server call is required, this mechanism is asynchronous. Therefore, you should either:

  * Provide a `completion` callback as an argument to the method to ensure you are notified when the data has been successfully fetched.
  * Use coroutines for asynchronous handling.
</Warning>

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    kameleoonClient.getRemoteData("key", result -> {
        try {
            JSONObject jsonObject = result.getOrThrow();
            // jsonObject contains result of request
        } catch (Exception ex) {
            // request failed with an exception
        }
    });
    ```

    ##### Parameters

    | Name                                                     | Type                                      | Description                                                 | Default |
    | -------------------------------------------------------- | ----------------------------------------- | ----------------------------------------------------------- | ------- |
    | key <Badge color="green" size="sm">optional</Badge>      | `String`                                  | The key that the data you're retrieving is associated with. | `null`  |
    | completion <Badge color="red" size="sm">required</Badge> | `ResultCompletion<JSONObject, Exception>` | The callback that processes the received data.              |         |
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    kameleoonClient.getRemoteData("key") { result ->
        try {
            val jsonObject: JSONObject = result.getOrThrow()
            // jsonObject contains result of request
        } catch (ex: Exception) {
            // request failed with an exception
        }
    }
    ```

    ##### Parameters

    | Name                                                     | Type                                      | Description                                                 | Default |
    | -------------------------------------------------------- | ----------------------------------------- | ----------------------------------------------------------- | ------- |
    | key <Badge color="green" size="sm">optional</Badge>      | `String`                                  | The key that the data you're retrieving is associated with. | `null`  |
    | completion <Badge color="red" size="sm">required</Badge> | `ResultCompletion<JSONObject, Exception>` | The callback that processes the received data.              |         |
  </Tab>

  <Tab title="Kotlin (Coroutines)">
    <Warning>
      A common mistake is using suspended functions inside `mapCatching`, `runCatching`, or a `try-catch` block without properly re-throwing `CancellationException`, which can interfere with coroutine cancellation. To ensure correct behavior, try to avoid calling suspend functions within these blocks.
    </Warning>

    ```kotlin theme={null}
    viewModelScope.launch {
        val jsonObject = kameleoonClient.getRemoteData("key").getOrNull() ?: return@launch
    }
    ```

    ##### Parameters

    | Name                                                | Type     | Description                                                 | Default |
    | --------------------------------------------------- | -------- | ----------------------------------------------------------- | ------- |
    | key <Badge color="green" size="sm">optional</Badge> | `String` | The key that the data you're retrieving is associated with. | `null`  |

    ##### Return value

    | Type                 | Description                                                                                             |
    | -------------------- | ------------------------------------------------------------------------------------------------------- |
    | `Result<JSONObject>` | A Kotlin `Result` that contains either the fetched value (`JSONObject`) or the exception that occurred. |
  </Tab>
</Tabs>

#### getRemoteVisitorData()

* 🔄 *Performs an asynchronous request*

`getRemoteVisitorData()` is an asynchronous method for retrieving Kameleoon Visits Data for the visitor from the Kameleoon Data API. The method adds data to storage for other methods to use when making targeting decisions.

Data obtained using this method plays an important role when you want to:

* use data collected from other devices.
* access a user's history, such as custom data collected during previous visits.

Read [this article](/developer-docs/feature-experimentation/targeting-and-segmentation/native-segmentation) for a better understanding of possible use cases.

<Info>
  By default, `getRemoteVisitorData()` automatically retrieves the latest stored custom data with `scope=Visitor` and attaches it to the visitor without having to call the method `addData()`. It is particularly useful for [synchronizing custom data between multiple devices](/developer-docs/sdks/web-sdks/nodejs-sdk#synchronizing-custom-data-across-devices).

  Checking only for failed results is recommended. However, if necessary, it can be verified that the data has been added to the visitor and is available for targeting purposes (or for debugging, though using [logging](#logging) is better for debugging). Additionally, data can be managed manually if the `shouldAddData=false` parameter is passed.
</Info>

<Warning>
  Since a server call is required, this mechanism is asynchronous. Therefore, you should either:

  * Provide a `completion` callback as an argument to the method to ensure you are notified when the data has been successfully fetched and added to the visitor.
  * Use coroutines for asynchronous handling.
</Warning>

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    // Visitor data will be fetched and automatically added for `visitorCode`.
    kameleoonClient.getRemoteVisitorData(result -> {
        if (result.isSuccess()) {
            // Data was successfully retrieved from the Kameleoon servers and added to the visitor.
        } else {
            // The request failed due to an exception.
        }
    });

    // If you only want to fetch data and add it yourself manually, set shouldAddData == `false`.
    kameleoonClient.getRemoteVisitorData(false, result -> {
        try {
            List<Data> visitorData = result.getOrThrow();
            // visitorData contains the fetched visitor data from Kameleoon servers, which can be manually added.
        } catch (Exception ex) {
            // The request failed due to an exception.
        }
    });

    // If you want to fetch custom list of data types
    RemoteVisitorDataFilter filter = RemoteVisitorDataFilter.builder()
            .previousVisitAmount(25)
            .experiments(true)
            .conversion(true)
            .build();
    kameleoonClient.getRemoteVisitorData(filter, result -> {
        if (result.isSuccess()) {
            // Data was successfully retrieved from the Kameleoon servers and added to the visitor.
        } else {
            // The request failed due to an exception.
        }
    });
    ```

    ##### Parameters

    | Name                                                          | Type                                      | Description                                                                                                                                                      | Default |
    | ------------------------------------------------------------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
    | filter <Badge color="green" size="sm">optional</Badge>        | `RemoteVisitorDataFilter`                 | Filter that selects which data should be retrieved from visit history. By default, the method retrieves `CustomData` from the current and latest previous visit. | `null`  |
    | shouldAddData <Badge color="green" size="sm">optional</Badge> | `boolean`                                 | A boolean indicating whether the method should automatically add retrieved data for a visitor.                                                                   | `true`  |
    | completion <Badge color="red" size="sm">required</Badge>      | `ResultCompletion<List<Data>, Exception>` | The callback that processes the received visitor data.                                                                                                           |         |
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    // Visitor data will be fetched and automatically added to the visitor.
    kameleoonClient.getRemoteVisitorData { result ->
        if (result.isSuccess) {
            // Data was successfully retrieved from the Kameleoon servers and added to the visitor.
        } else {
            // The request failed due to an exception.
        }
    }

    // If you only want to fetch data and add it yourself manually, set shouldAddData == `false`
    kameleoonClient.getRemoteVisitorData(false) { result ->
        try {
            val visitorData = result.getOrThrow()
            // visitorData contains the fetched visitor data from Kameleoon servers, which can be manually added.
        } catch (e: Exception) {
            // The request failed due to an exception.
        }
    }

    val filter = RemoteVisitorDataFilter.builder()
        .previousVisitAmount(25)
        .experiments(true)
        .conversion(true)
        .build()
    kameleoonClient.getRemoteVisitorData(filter) { result ->
        if (result.isSuccess) {
            // Data was successfully retrieved from the Kameleoon servers and added to the visitor.
        } else {
            // The request failed due to an exception.
        }
    }
    ```

    ##### Parameters

    | Name                                                          | Type                                      | Description                                                                                                                                                      | Default |
    | ------------------------------------------------------------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
    | filter <Badge color="green" size="sm">optional</Badge>        | `RemoteVisitorDataFilter`                 | Filter that selects which data should be retrieved from visit history. By default, the method retrieves `CustomData` from the current and latest previous visit. | `null`  |
    | shouldAddData <Badge color="green" size="sm">optional</Badge> | `Boolean`                                 | A boolean indicating whether the method should automatically add retrieved data for a visitor.                                                                   | `true`  |
    | completion <Badge color="red" size="sm">required</Badge>      | `ResultCompletion<List<Data>, Exception>` | The callback that processes the received visitor data.                                                                                                           |         |
  </Tab>

  <Tab title="Kotlin (Coroutines)">
    <Warning>
      A common mistake is using suspended functions inside `mapCatching`, `runCatching`, or a `try-catch` block without properly re-throwing `CancellationException`, which can interfere with coroutine cancellation. To ensure correct behavior, try to avoid calling suspend functions within these blocks.
    </Warning>

    ```kotlin theme={null}
    // Visitor data will be fetched and automatically added for `visitorCode`
    viewModelScope.launch {
        kameleoonClient.getRemoteVisitorData() ?: return@launch
    }

    // If you only want to fetch data and add it yourself manually, set shouldAddData == `false`
    viewModelScope.launch {
        kameleoonClient.getRemoteVisitorData(shouldAddData = false)
            .onSuccess { visitorData ->
                // visitorData contains the fetched visitor data from Kameleoon servers, which can be manually added.
            }.onFailure { ex ->
                // request failed with exception
            }
    }

    viewModelScope.launch {
        val filter = RemoteVisitorDataFilter.builder()
            .previousVisitAmount(25)
            .experiments(true)
            .conversion(true)
            .build()
        // In general, we recommend checking only if the request fails.
        kameleoonClient.getRemoteVisitorData(filter) ?: return@launch
    }
    ```

    ##### Parameters

    | Name                                                          | Type                      | Description                                                                                                                                                      | Default |
    | ------------------------------------------------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
    | filter <Badge color="green" size="sm">optional</Badge>        | `RemoteVisitorDataFilter` | Filter that selects which data should be retrieved from visit history. By default, the method retrieves `CustomData` from the current and latest previous visit. | `null`  |
    | shouldAddData <Badge color="green" size="sm">optional</Badge> | `Boolean`                 | A boolean indicating whether the method should automatically add retrieved data for a visitor.                                                                   | `true`  |

    ##### Return value

    | Type                 | Description                                                                                             |
    | -------------------- | ------------------------------------------------------------------------------------------------------- |
    | `Result<List<Data>>` | A Kotlin `Result` that contains either the fetched value (`List<Data>`) or the exception that occurred. |
  </Tab>
</Tabs>

##### Using parameters of `RemoteVisitorDataFilter`

The `getRemoteVisitorData()` method offers flexibility by letting you define various parameters when retrieving data on visitors. Whether you're targeting based on goals, experiments, or variations, the same approach applies across all data types.

For example, suppose you want to retrieve data on visitors who completed a goal "Order transaction". You can specify parameters within the `getRemoteVisitorData()` method to refine your targeting. For instance, if you want to target only users who converted on the goal in their last five visits, you can set the `previousVisitAmount` parameter to `5` and `conversions` to `true`.

The flexibility shown in this example is not limited to goal data. You can use parameters within the `getRemoteVisitorData()` method to retrieve data on a variety of visitor behaviors.

<Note>
  Here is the list of available `RemoteVisitorDataFilter` options:

  | Name                                                                | Type      | Description                                                                                                                                                                                                                                                                                                                                     | Default |
  | ------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
  | previousVisitAmount <Badge color="green" size="sm">optional</Badge> | `int`     | Number of previous visits to retrieve data from. Number between `1` and `25`                                                                                                                                                                                                                                                                    | `1`     |
  | currentVisit <Badge color="green" size="sm">optional</Badge>        | `boolean` | If true, current visit data will be retrieved                                                                                                                                                                                                                                                                                                   | `true`  |
  | customData <Badge color="green" size="sm">optional</Badge>          | `boolean` | If true, custom data will be retrieved.                                                                                                                                                                                                                                                                                                         | `true`  |
  | geolocation <Badge color="green" size="sm">optional</Badge>         | `boolean` | If true, geolocation data will be retrieved.                                                                                                                                                                                                                                                                                                    | `false` |
  | conversions <Badge color="green" size="sm">optional</Badge>         | `boolean` | If true, conversion data will be retrieved.                                                                                                                                                                                                                                                                                                     | `false` |
  | experiments <Badge color="green" size="sm">optional</Badge>         | `boolean` | If true, experiment data will be retrieved.                                                                                                                                                                                                                                                                                                     | `false` |
  | kcs <Badge color="green" size="sm">optional</Badge>                 | `boolean` | If true, Kameleoon Conversion Score (KCS) will be retrieved. Requires the [AI Predictive Targeting add-on](/user-manual/assets/segments/target-users-based-on-likelihood-to-convert)                                                                                                                                                            | `false` |
  | visitorCode <Badge color="green" size="sm">optional</Badge>         | `boolean` | If true, Kameleoon will retrieve the `visitorCode` from the most recent visit and use it for the current visit. This is necessary if you want to ensure that the visitor, identified by their `visitorCode`, always receives the same variation across visits for [Cross-device experimentation](/developer-docs/cross-device-experimentation). | `true`  |
  | personalization <Badge color="green" size="sm">optional</Badge>     | `boolean` | If true, personalization data will be retrieved. This is required for the personalization condition.                                                                                                                                                                                                                                            | `false` |
  | cbs <Badge color="green" size="sm">optional</Badge>                 | `boolean` | If true, Contextual Bandit score data will be retrieved.                                                                                                                                                                                                                                                                                        | `false` |
</Note>

#### getVisitorWarehouseAudience()

* 🔄 *Performs an asynchronous request*

Retrieves all audience data associated with the visitor in your data warehouse. The optional `warehouseKey` parameter is typically your internal user ID. The `customDataIndex` parameter corresponds to the Kameleoon custom data that Kameleoon uses to target your visitors. You can refer to the [warehouse targeting documentation](/user-manual/integrations/data-warehouses/bigquery/use-bigquery-as-a-source-audience-targeting) for additional details.

<Warning>
  Since a server call is required, this mechanism is asynchronous. Therefore, you should either:

  * Provide a `completion` callback as an argument to the method to ensure you are notified when the data has been successfully fetched and added to the visitor.
  * Use coroutines for asynchronous handling.

  Checking only for failed results is recommended. However, if necessary, it can be verified that the data has been added to the visitor and is available for targeting purposes (or for debugging, though using [logging](#logging) is better for debugging).
</Warning>

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    // Visitor data will be fetched and automatically added for the visitor
    kameleoonClient.getVisitorWarehouseAudience(customDataIndex, result -> {
        if (result.isSuccess()) {
            // Due to method called before this callback, data was automatically added to the visitor.
        } else {
            Exception exception = result.failure();
            // The request failed due to an exception.
        }
    });

    // If you need to specify warehouse key
    kameleoonClient.getVisitorWarehouseAudience("warehouseKey", customDataIndex, result -> {
        // Due to method called before this callback, data was automatically added to the visitor,
        // but you can evaluate the added data if necessary.
        try {
            CustomData data = result.getOrThrow();
        } catch (Exception exception) {
            // The request failed due to an exception.
        }
    });
    ```

    ##### Parameters

    | Name                                                          | Type                                      | Description                                                                                      | Default |
    | ------------------------------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------ | ------- |
    | warehouseKey <Badge color="green" size="sm">optional</Badge>  | `String`                                  | A unique key to identify the warehouse data (usually, your internal user ID).                    | `null`  |
    | customDataIndex <Badge color="red" size="sm">required</Badge> | `int`                                     | An integer representing the custom data index you want to use to target your BigQuery Audiences. |         |
    | completion <Badge color="red" size="sm">required</Badge>      | `ResultCompletion<CustomData, Exception>` | The callback that processes the received data.                                                   |         |
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    // Visitor data will be fetched and automatically added for the visitor
    kameleoonClient.getVisitorWarehouseAudience(customDataIndex) { result ->
        if (result.isSuccess) {
            // Due to method called before this callback, data was automatically added to the visitor.
        } else {
            val exception = result.failure()
            // The request failed due to an exception.
        }
    }

    // If you need to specify warehouse key
    kameleoonClient.getVisitorWarehouseAudience("warehouseKey", customDataIndex) { result ->
        // As a result of the method before this callback is called, data was automatically added to the visitor
        // but you can evaluate the added data if you need to check it.
        try {
            val data = result.getOrThrow()
        } catch (e: Exception) {
            // The request failed due to an exception.
        }
    }
    ```

    ##### Parameters

    | Name                                                          | Type                                      | Description                                                                                      | Default |
    | ------------------------------------------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------ | ------- |
    | warehouseKey <Badge color="green" size="sm">optional</Badge>  | `String`                                  | A unique key to identify the warehouse data (usually, your internal user ID).                    | `null`  |
    | customDataIndex <Badge color="red" size="sm">required</Badge> | `Int`                                     | An integer representing the custom data index you want to use to target your BigQuery Audiences. |         |
    | completion <Badge color="red" size="sm">required</Badge>      | `ResultCompletion<CustomData, Exception>` | The callback that processes the received data.                                                   |         |
  </Tab>

  <Tab title="Kotlin (Coroutines)">
    <Warning>
      A common mistake is using suspended functions inside `mapCatching`, `runCatching`, or a `try-catch` block without properly re-throwing `CancellationException`, which can interfere with coroutine cancellation. To ensure correct behavior, try to avoid calling suspend functions within these blocks.
    </Warning>

    ```kotlin theme={null}
    // Visitor data will be fetched and automatically added for the visitor
    viewModelScope.launch {
        // As a result of the method call, the data was automatically added to the visitor.
        kameleoonClient.getVisitorWarehouseAudience(customDataIndex = customDataIndex) ?: return@launch
    }

    // If you need to specify warehouse key
    viewModelScope.launch {
        // As a result of the method call, the data was automatically added to the visitor.
        kameleoonClient.getVisitorWarehouseAudience("warehouseKey", customDataIndex)
            .onSuccess { customData ->
                // But you can evaluate the added data if you need to check it.
            }
            .onFailure { ex ->
                // The request failed due to an exception.
            }
    }
    ```

    ##### Parameters

    | Name                                                          | Type     | Description                                                                                      | Default |
    | ------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------ | ------- |
    | warehouseKey <Badge color="green" size="sm">optional</Badge>  | `String` | A unique key indentifying the warehouse data (usually your internal user ID).                    | `null`  |
    | customDataIndex <Badge color="red" size="sm">required</Badge> | `Int`    | An integer representing the custom data index you want to use to target your BigQuery Audiences. |         |

    ##### Return value

    | Type                 | Description                                                                                             |
    | -------------------- | ------------------------------------------------------------------------------------------------------- |
    | `Result<CustomData>` | A Kotlin `Result` that contains either the fetched value (`CustomData`) or the exception that occurred. |
  </Tab>
</Tabs>

#### setLegalConsent()

You must use this method to specify whether the visitor has given legal consent to use their personal data. Setting the `legalConsent` parameter to `false` limits the types of data that you can include in tracking requests. This method helps you adhere to legal and regulatory requirements while responsibly managing visitor data. You can find more information on personal data in the [consent management policy](/user-manual/project-management/consent-management-policy).

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    kameleoonClient.setLegalConsent(true);
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    kameleoonClient.setLegalConsent(true)
    ```
  </Tab>
</Tabs>

##### Parameters

| Name         | Type    | Description                                                                                                                                                                                                             |
| ------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| legalConsent | boolean | A boolean value representing the legal consent status. `true` indicates the visitor has given legal consent, `false` indicates the visitor has never provided, or has withdrawn, legal consent. This field is required. |

### Data types

This section lists the `com.Kameleoon.Data` types supported by Kameleoon. Several standard data types are provided, as well as the `CustomData` type for defining custom data types.

#### Conversion

The `Conversion` data set stored here can be used to filter experiment and personalization reports by any goal associated with it.

<Tip>
  * Each visitor can have multiple `Conversion` objects.
  * You can find the `goalId` in the Kameleoon app.
</Tip>

| Name                                                       | Type            | Description                                     | Default             |
| ---------------------------------------------------------- | --------------- | ----------------------------------------------- | ------------------- |
| `goalId` <Badge color="red" size="sm">required</Badge>     | `int`           | ID of the goal.                                 |                     |
| `revenue` <Badge color="green" size="sm">optional</Badge>  | `float`         | Revenue of the conversion                       | `0`                 |
| `negative` <Badge color="green" size="sm">optional</Badge> | `boolean`       | Defines if the revenue is positive or negative. | `false`             |
| `metadata` <Badge color="green" size="sm">optional</Badge> | `CustomData...` | Metadata of the conversion.                     | `new CustomData[0]` |

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    kameleoonClient.addData(new Conversion(32, 10f));

    kameleoonClient.addData(new Conversion(33, 0f, true));

    kameleoonClient.addData(
        new Conversion(34, 5f, new CustomData(3, "metadata1", "md2"), new CustomData(5, "md3"))
    );
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    kameleoonClient.addData(Conversion(32, 10f))

    kameleoonClient.addData(Conversion(33, 0f, true))

    kameleoonClient.addData(
        Conversion(34, 5f, CustomData(3, "metadata1", "md2"), CustomData(5, "md3"))
    )
    ```
  </Tab>
</Tabs>

#### Device

<Note>
  Since Android SDK `4.13.0`, the `Device` is automatically detected based on the [`android.content.Context`](https://developer.android.com/reference/android/content/Context). However, you can still manually override it if needed.
</Note>

Store information about the user's device.

| Name                                                 | Type      | Description                                          |
| ---------------------------------------------------- | --------- | ---------------------------------------------------- |
| device <Badge color="red" size="sm">required</Badge> | `Devices` | List of devices: **phone**, **tablet**, **desktop**. |

<Tabs defaultTabIndex={0}>
  <Tab title="Java">
    ```java theme={null}
    kameleoonClient.addData(Device.tablet());
    ```
  </Tab>
</Tabs>

#### Geolocation

`Geolocation` contains the visitor's geolocation details.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    kameleoonClient.addData(new Geolocation("France", "Île-de-France", "Paris"));
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    kameleoonClient.addData(Geolocation("France", "Île-de-France", "Paris"))
    ```
  </Tab>
</Tabs>

| Name                                                         | Type                  | Description                                                                                                      |
| ------------------------------------------------------------ | --------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `country` <Badge color="red" size="sm">required</Badge>      | `String`              | The country of the visitor.                                                                                      |
| `region` <Badge color="green" size="sm">optional</Badge>     | <nobr>`String`</nobr> | The region of the visitor.                                                                                       |
| `city` <Badge color="green" size="sm">optional</Badge>       | <nobr>`String`</nobr> | The city of the visitor.                                                                                         |
| `postalCode` <Badge color="green" size="sm">optional</Badge> | <nobr>`String`</nobr> | The postal code of the visitor.                                                                                  |
| `latitude` <Badge color="green" size="sm">optional</Badge>   | `float`               | The latitude coordinate representing the location of the visitor. Coordinate number represents decimal degrees.  |
| `longitude` <Badge color="green" size="sm">optional</Badge>  | `float`               | The longitude coordinate representing the location of the visitor. Coordinate number represents decimal degrees. |

<Tip>
  * Each visitor can have only one `Geolocation`. Adding a second `Geolocation` overwrites the first one.
</Tip>

#### CustomData

Define your own custom data types in the Kameleoon app or the Data API and use them from the SDK.

| Name                                                      | Type                             | Description                                                                                                                                                                             | Default |
| --------------------------------------------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| index/name <Badge color="red" size="sm">required</Badge>  | `int`/`String`                   | Index or Name of the custom data. **Either `index` or `name` must be provided** to identify the data.                                                                                   |         |
| values <Badge color="red" size="sm">required</Badge>      | `String...`/`Collection<String>` | Values of the custom data to be stored.                                                                                                                                                 |         |
| overwrite <Badge color="green" size="sm">optional</Badge> | `boolean`                        | Flag to explicitly control how the values are stored and how they appear in reports. [See more](/developer-docs/custom-data#default-logic-when-overwrite-parameter-is-false-or-omitted) | `true`  |

<Note>
  * The index of the custom data is available in the **Custom data configuration** page of the Kameleoon app. Be careful: this index starts at 0, so the first custom data you create for a given site would have the index 0, not 1.
  * Adding a `CustomData` instance created with a name when the SDK instance configuration is not up to date or the name is not registered, will result in the data being ignored.
</Note>

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    kameleoonClient.addData(new CustomData(1, "value"));

    // With several values
    kameleoonClient.addData(new CustomData(1, "value1", "value2"));

    // To set the 'overwrite' flag to false
    kameleoonClient.addData(new CustomData(1, false, "value"));

    // To use a name instead of the index
    kameleoonClient.addData(new CustomData("my-custom-data", "value"));
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    kameleoonClient.addData(CustomData(1, "value"))

    // With several values
    kameleoonClient.addData(CustomData(1, "value1", "value2"))

    // To set the 'overwrite' flag to false
    kameleoonClient.addData(CustomData(1, false, "value"))

    // To use a name instead of the index
    kameleoonClient.addData(CustomData("my-custom-data", "value"))
    ```
  </Tab>
</Tabs>

### Returned Types

#### DataFile

The `DataFile` contains the SDK configuration details.

It can be extended with additional information if required by clients. If you need more details, please contact your Customer Success Manager.

| Name           | Type                       | Description                                                                       |
| -------------- | -------------------------- | --------------------------------------------------------------------------------- |
| `featureFlags` | `Map<String, FeatureFlag>` | A map of [`FeatureFlag`](#featureflag) objects, keyed by feature flag keys.       |
| `dateModified` | `long`                     | The timestamp (in milliseconds) indicating when the `DataFile` was last modified. |

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    // Retrieves the map of feature flags from the DataFile.
    // The map is keyed by feature flag identifiers, with each value being a FeatureFlag object.
    Map<String, FeatureFlag> featureFlags = dataFile.getFeatureFlags();

    // Retrieves the last modification timestamp of the DataFile.
    // The value is a long representing milliseconds since the Unix epoch.
    long dateModified = dataFile.getDateModified();
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    // Retrieves the map of feature flags from the DataFile.
    // The map is keyed by feature flag identifiers, with each value being a FeatureFlag object.
    val featureFlags = dataFile.featureFlags

    // Retrieves the last modification timestamp of the DataFile.
    // The value is a long representing milliseconds since the Unix epoch.
    val dateModified = dataFile.dateModified
    ```
  </Tab>
</Tabs>

#### FeatureFlag

The `FeatureFlag` represents a set of properties that define a feature flag itself — for example, its [`Variations`](#variation), [`Rules`](#rule), environment status, and other related details.

It can be extended with additional information if required by clients. If you need more details, please contact your Customer Success Manager.

| Name                  | Type                     | Description                                                                |
| --------------------- | ------------------------ | -------------------------------------------------------------------------- |
| `environmentEnabled`  | `boolean`                | Indicating whether the feature flag is enabled in the current environment. |
| `defaultVariationKey` | `String`                 | The key of the default variation associated with the feature flag.         |
| `variations`          | `Map<String, Variation>` | A map of `Variation` objects, keyed by variation keys.                     |
| `rules`               | `List<Rule>`             | A list of `Rule` objects                                                   |

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    // Check whether the feature flag is enabled in the current environment
    boolean isEnvironmentEnabled = featureFlag.isEnvironmentEnabled();

    // Retrieve the key of the default variation
    String defaultVariationKey = featureFlag.getDefaultVariationKey();

    // Retrieve the default variation object
    Variation defaultVariation = featureFlag.getDefaultVariation();

    // Retrieve all variations of the feature flag as a map (key = variation key, value = Variation object)
    Map<String, Variation> variations = featureFlag.getVariations();

    // Retrieve all targeting rules associated with the feature flag
    List<Rule> rules = featureFlag.getRules();
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    // Check whether the feature flag is enabled in the current environment
    val isEnvironmentEnabled = featureFlag.isEnvironmentEnabled

    // Retrieve the key of the default variation
    val defaultVariationKey = featureFlag.defaultVariationKey

    // Retrieve the default variation object
    val defaultVariation = featureFlag.defaultVariation

    // Retrieve all variations of the feature flag as a map (key = variation key, value = Variation object)
    val variations = featureFlag.variations

    // Retrieve all targeting rules associated with the feature flag
    val rules = featureFlag.rules
    ```
  </Tab>
</Tabs>

#### Rule

The `Rule` represents a set of properties that define a rule itself — for example, its [`Variations`](#variation).

It can be extended with additional information if required by clients. If you need more details, please contact your Customer Success Manager.

| Name         | Type                     | Description                                            |
| ------------ | ------------------------ | ------------------------------------------------------ |
| `variations` | `Map<String, Variation>` | A map of `Variation` objects, keyed by variation keys. |

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    // Retrieve all variations of the rule as a map (key = variation key, value = Variation object)
    Map<String, Variation> variations = rule.getVariations();
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    // Retrieve all variations of the rule as a map (key = variation key, value = Variation object)
    val variations = rule.variations
    ```
  </Tab>
</Tabs>

#### Variation

`Variation` contains information about the assigned variation to the visitor (or the default variation, if no specific assignment exists).

| Name         | Type                    | Description                                                                                                                                          |
| ------------ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| name         | `String`                | The name of the variation.                                                                                                                           |
| key          | `String`                | The unique key identifying the variation.                                                                                                            |
| id           | `Integer`               | The ID of the assigned variation (or `null` if it's the default variation).                                                                          |
| experimentId | `Integer`               | The ID of the experiment associated with the variation (or `null` if default).                                                                       |
| variables    | `Map<String, Variable>` | A map containing the variables of the assigned variation, keyed by variable names. This could be an empty collection if no variables are associated. |

<Note>
  * The `Variation` object provides details about the assigned variation and its associated experiment, while the [`Variable`](#variable) object contains specific details about each variable within a variation.
  * Ensure that your code handles the case where `id` or `experimentId` may be `null`, indicating a default variation.
  * The `variables` map might be empty if no variables are associated with the variation.
</Note>

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    // Retrieving the variation name
    String variationName = variation.getName();

    // Retrieving the variation key
    String variationKey = variation.getKey();

    // Retrieving the variation id
    Integer variationId = variation.getId();

    // Retrieving the experiment id
    Integer experimentId = variation.getExperimentId();

    // Retrieving the variables map
    Map<String, Variable> variables = variation.getVariables();
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    // Retrieving the variation name
    val variationName = variation.name

    // Retrieving the variation key
    val variationKey = variation.key

    // Retrieving the variation id
    val variationId = variation.id

    // Retrieving the experiment id
    val experimentId = variation.experimentId

    // Retrieving the variables map
    val variables = variation.variables
    ```
  </Tab>
</Tabs>

#### Variable

`Variable` contains information about a variable associated with the assigned variation.

| Name  | Type     | Description                                                                                                                                                      |
| ----- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| key   | `String` | The unique key identifying the variable.                                                                                                                         |
| type  | `String` | The type of the variable. Possible values: **BOOLEAN**, **NUMBER**, **STRING**, **JSON**.                                                                        |
| value | `Object` | The value of the variable, which can be of the following types: **boolean**, **int**, **long**, **double**, **String**, **JSONObject**, **JSONArray**, **null**. |

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    // Retrieving the variables map
    Map<String, Variable> variables = variation.getVariables();

    // Variable type can be retrieved for further processing
    String type = variables.get("isDiscount").getType();

    // Get the Boolean value of "isDiscount"
    Boolean isDiscount = (Boolean) variables.get("isDiscount").getValue();

    // Get the numeric value of "number" as an Integer
    Integer number = (Integer) variables.get("number").getValue();

    // Get the String value of "title"
    String title = (String) variables.get("title").getValue();
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    // Retrieving the variables map
    val variables = variation.variables

    // Variable type can be retrieved for further processing
    val type = variables["isDiscount"]?.type

    // Get the Boolean value of "isDiscount"
    val isDiscount = variables["isDiscount"]?.value as? Boolean

    // Get the numeric value of "number" as an Integer
    val number = variables.get("number").value as? Int

    // Get the String value of "title"
    val title = variables["title"]?.value as? String
    ```
  </Tab>
</Tabs>

### Deprecated methods

<Warning>
  These methods are deprecated and will be removed in SDK version `5.0.0`.
</Warning>

#### getFeatureVariationKey()

* 📨 *Sends Tracking Data to Kameleoon*

<Note>
  Use [`getVariation()`](#getvariation) instead.
</Note>

Use this method to get the feature variation key for a visitor. This method takes a `featureKey` as a required argument to retrieve the variation key for the specified user.

If the visitor has never been associated with this feature flag, the SDK returns a randomly assigned variation key (according to the feature flag rules). If the visitor is already registered with this feature flag, this method returns the previous variation key. If the user does not match any of the rules, the default value will be returned, which is defined in your customer's account.

Ensure you set up proper error handling as shown in the example code to catch potential exceptions.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    String featureKey = "new_checkout";
    String variationKey = "";

    try {
        variationKey = kameleoonClient.getFeatureVariationKey(featureKey);
    } catch (KameleoonException.SDKNotReady e) {
        // Exception indicates that the SDK has not completed its initialization yet.
    } catch (KameleoonException.FeatureNotFound e) {
        // The error has occurred; feature flag isn't found in current configuration.
    } catch (KameleoonException.FeatureEnvironmentDisabled e) {
        // The feature flag is disabled for the environment
    } catch (Exception e) {
        // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
    }

    switch (variationKey) {
        case "on":
            //main variation key is selected for visitorCode
            break;
        case "alternative_variation":
            //alternative variation key
            break;
        default:
            //default variation key
            break;
    }
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    val featureKey = "new_checkout"
    var variationKey = ""

    try {
        variationKey = kameleoonClient.getFeatureVariationKey(featureKey)
    } catch (e: KameleoonException.SDKNotReady) {
        // Exception indicates that the SDK has not completed its initialization yet.
    } catch (e: KameleoonException.FeatureNotFound) {
        // Exception indicates that the SDK not initialized or the feature toggle is not yet activated on Kameleoon's side. We consider the feature inactive.
    } catch (e: KameleoonException.FeatureEnvironmentDisabled) {
        // The feature flag is disabled for the environment
    } catch (e: Exception) {
        // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
    }

    when (variationKey) {
        "on" -> {}
        "alternative_variation" -> {}
        else -> {}
    }
    ```
  </Tab>
</Tabs>

#### getFeatureVariationKey()

* 📨 *Sends Tracking Data to Kameleoon*

<Note>
  Use [`getVariation()`](#getvariation) instead.
</Note>

Use this method to get the feature variation key for a visitor. This method takes a `featureKey` as a required argument to retrieve the variation key for the specified user.

If the visitor has never been associated with this feature flag, the SDK returns a randomly assigned variation key (according to the feature flag rules). If the visitor is already registered with this feature flag, this method returns the previous variation key. If the user does not match any of the rules, the default value will be returned, which is defined in your customer's account.

Ensure you set up proper error handling as shown in the example code to catch potential exceptions.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    String featureKey = "new_checkout";
    String variationKey = "";

    try {
        variationKey = kameleoonClient.getFeatureVariationKey(featureKey);
    } catch (KameleoonException.SDKNotReady e) {
        // Exception indicates that the SDK has not completed its initialization yet.
    } catch (KameleoonException.FeatureNotFound e) {
        // The error has occurred; feature flag isn't found in current configuration.
    } catch (KameleoonException.FeatureEnvironmentDisabled e) {
        // The feature flag is disabled for the environment
    } catch (Exception e) {
        // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
    }

    switch (variationKey) {
        case "on":
            //main variation key is selected for visitorCode
            break;
        case "alternative_variation":
            //alternative variation key
            break;
        default:
            //default variation key
            break;
    }
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    val featureKey = "new_checkout"
    var variationKey = ""

    try {
        variationKey = kameleoonClient.getFeatureVariationKey(featureKey)
    } catch (e: KameleoonException.SDKNotReady) {
        // Exception indicates that the SDK has not completed its initialization yet.
    } catch (e: KameleoonException.FeatureNotFound) {
        // Exception indicates that the SDK not initialized or the feature toggle is not yet activated on Kameleoon's side. We consider the feature inactive.
    } catch (e: KameleoonException.FeatureEnvironmentDisabled) {
        // The feature flag is disabled for the environment
    } catch (e: Exception) {
        // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
    }

    when (variationKey) {
        "on" -> {}
        "alternative_variation" -> {}
        else -> {}
    }
    ```
  </Tab>
</Tabs>

#### getActiveFeatures()

<Note>
  * Use [`getVariations()`](#getvariations) instead.
  * Previously called `getFeatureListForVisitorCode`, which was removed in SDK version `4.0.0` release.
</Note>

`getActiveFeatures` method retrieves information about the active feature flags that are available for the visitor.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    Map<String, Variation> listActiveFeatureFlags = kameleoonClient.getActiveFeatures();
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    val listActiveFeatureFlags = kameleoonClient.getActiveFeatures()
    ```
  </Tab>
</Tabs>

##### Return value

| Type                     | Description                                                                                                                    |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `Map<String, Variation>` | A dictionary that contains the assigned variations of the active features using the keys of the corresponding active features. |

#### getFeatureVariable()

* 📨 *Sends Tracking Data to Kameleoon*

<Note>
  Use [`getVariation()`](#getvariation) instead.
</Note>

This method gets a variable value of variation key for a specific user. It takes a `featureKey`, and `variableKey` as required arguments.

If the visitor has never been associated with the `featureKey`, the SDK returns a randomly assigned variable value for the specified variation key (according to the feature flag rules). If the visitor is already registered with this feature flag, the method returns the variable value for the previously registered variation. If the user does not match any of the rules, the default variable value is returned.

Ensure you set up proper error handling as shown in the example code to catch potential exceptions.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    String featureKey = "feature_key";
    String variableKey = "variableKey";

    try {
        Object variableValue = kameleoonClient.getFeatureVariable(featureKey, variableKey);
        // your custom code, depending on variableValue
    } catch (KameleoonException.SDKNotReady e) {
        // Exception indicates that the SDK has not completed its initialization yet.
    } catch (KameleoonException.FeatureNotFound e) {
        // The error has occurred; feature flag isn't found in current configuration.
    } catch (KameleoonException.FeatureVariableNotFound e) {
        // Requested variable not defined in Kameleoon
    } catch (KameleoonException.FeatureEnvironmentDisabled e) {
        // The feature flag is disabled for the environment.
    } catch (Exception e) {
        // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
    }
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    val featureKey = "new_checkout"
    val variableKey = "var"

    try {
        val variableValue = kameleoonClient.getFeatureVariable(featureKey, variableKey)
        // your custom code depending on variableValue
    } catch (e: KameleoonException.SDKNotReady) {
        // Exception indicating that the SDK has not completed its initialization yet.
    } catch (e: KameleoonException.FeatureNotFound) {
        // The error has happened; feature flag isn't found in current configuration
    } catch (e: KameleoonException.FeatureVariableNotFound) {
        // Requested variable not defined on Kameleoon's side
    } catch (e: KameleoonException.FeatureEnvironmentDisabled) {
        // The feature flag is disabled for the environment
    } catch (e: Exception) {
        // Recommended (but optional) safeguard for unexpected exceptions from third-party libraries
    }
    ```
  </Tab>
</Tabs>

##### Parameters

| Name         | Type   | Description                                                               |
| ------------ | ------ | ------------------------------------------------------------------------- |
| featureKey   | String | Key of the feature you want to display to a user. This field is required. |
| variableName | String | Name of the variable you want to get a value for. This field is required. |

##### Return value

| Type   | Description                                                                                                                                                                        |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| object | Value of variation variable that is registered for the specified `visitorCode` for this feature flag. Valid types: `boolean`, `int`, `double`, `String`, `JSONObject`, `JSONArray` |

##### Exceptions thrown

| Type                       | Description                                                                                                                                                                                                                                                                      |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SDKNotReady                | Exception indicating that the SDK has not completed its initialization.                                                                                                                                                                                                          |
| FeatureNotFound            | Exception indicating that the requested feature key was not found in the SDK's internal configuration. This exception usually means that the feature flag has not been activated on Kameleoon's side (but code implementing the feature is already deployed in the application). |
| FeatureVariableNotFound    | Exception indicating that the specified variable was not found. Check that the variable key in the Kameleoon app matches the one in your code.                                                                                                                                   |
| FeatureEnvironmentDisabled | Exception indicating that feature flag is disabled for the visitor's current environment (for example, production, staging, or development).                                                                                                                                     |

#### getFeatureVariationVariables()

<Note>
  * Use [`getVariation()`](#getvariation) instead.
  * This method was previously called `getFeatureAllVariables`, which was removed in SDK version `4.0.0` release.
</Note>

To retrieve all of a feature's variables, call this method. You can modify your feature variables in the Kameleoon app.

This method takes one input parameter: `featureKey`. It returns the data as a `Map<String, Object>` type, as defined in the Kameleoon app. It throws an exception (`FeatureNotFound`) if the requested feature was not found in the SDK's internal configuration.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    String featureKey = "myFeature";
    String variationKey = "variation1";

    try {
        Map<String, Object> variables = kameleoonClient.getFeatureVariationVariables(featureKey, variationKey);
    } catch (KameleoonException.SDKNotReady e) {
        // Exception indicating that the SDK has not completed its initialization yet.
    } catch (KameleoonException.FeatureNotFound e) {
        // The feature is not yet activated on Kameleoon's side
    } catch (KameleoonException.FeatureEnvironmentDisabled e) {
        // The feature flag is disabled for the environment
    } catch (Exception e) {
        // This is a generic Exception handler which will handle all exceptions.
        System.out.println("Exception occurred");
    }
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    val featureKey = "myFeature"
    val variationKey = "variation1"

    try {
        val variables = kameleoonClient.getFeatureVariationVariables(featureKey, variationKey)
    } catch (e: KameleoonException.SDKNotReady) {
        // Exception indicating that the SDK has not completed its initialization yet.
    } catch (e: KameleoonException.FeatureNotFound) {
        // The feature is not yet activated on Kameleoon's side
    } catch (e: KameleoonException.FeatureEnvironmentDisabled) {
        // The feature flag is disabled for the environment
    } catch (e: Exception) {
        // This is a generic Exception handler which will handle all exceptions.
        println("Exception occurred")
    }
    ```
  </Tab>
</Tabs>

##### Parameters

| Name       | Type   | Description                                                                  |
| ---------- | ------ | ---------------------------------------------------------------------------- |
| featureKey | String | Unique identifier of the feature you need to obtain. This field is required. |

##### Return value

| Type                 | Description                                                                                                                                                                                     |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Map<String,Object>` | Data representing the variables associated with this feature flag. Values can be `int`, `String`, `boolean`, `JSONObject` or `JSONArray` (depending on the types defined in the web interface). |

##### Exceptions thrown

| Type                       | Description                                                                                                                                                                                           |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SDKNotReady                | Exception indicating that the SDK has not completed its initialization.                                                                                                                               |
| FeatureNotFound            | Exception indicating that the requested feature was not found in the SDK's internal configuration. This exception usually means that the feature flag has not yet been activated on Kameleoon's side. |
| FeatureVariableNotFound    | Exception indicating that the specified variable was not found. Check that the variable key in the Kameleoon app matches the key in your code.                                                        |
| FeatureEnvironmentDisabled | Exception indicating that feature flag is disabled for the visitor's current environment (for example, production, staging, or development).                                                          |

#### getFeatureList()

<Tip>
  If you want to iterate over all feature flags and call [`getVariation()`](#getvariation) on each, use the [`getVariations()`](#getvariations) method instead.
</Tip>

Returns a list of feature flag keys currently available for the SDK.

<Tabs defaultTabIndex={1}>
  <Tab title="Java">
    ```java theme={null}
    List<String> allFeatureFlagListId = kameleoonClient.getFeatureList();
    ```
  </Tab>

  <Tab title="Kotlin">
    ```kotlin theme={null}
    val allFeatureFlagListId = kameleoonClient.getFeatureList()
    ```
  </Tab>
</Tabs>

##### Return value

| Type           | Description               |
| -------------- | ------------------------- |
| `List<String>` | List of feature flag keys |
