---
title: Configure Amplitude
slug: feature-experimentation/docs/set-up-amplitude
docTags: 
createdAt: 2026-08-05T20:39:57.000Z
---

:::hint{type="info"}
This is a third-party integration and is not an Optimizely subprocessor. See Optimizely's [Third-Party Add-Ons & Platform Integration Terms](https://www.optimizely.com/legal/third-party-add-ons-platform-integration-terms/).
:::

## Prerequisites

Before you configure this integration, you need the following:

- An Optimizely Feature Experimentation project with at least one flag.
- The Feature Experimentation SDK installed and initialized in your application.
- An Amplitude project and API key. See the [Amplitude Get Started documentation](https://amplitude.com/docs/get-started).
- The Amplitude SDK for your language installed. See the [Amplitude SDK catalog](https://amplitude.com/docs/sdks).

## Amplitude

Amplitude offers two property types to enrich and segment reports with your Feature Experimentation experiment and targeted delivery information.

- **Event properties** – Describes a single event and must be added to each logged event. Amplitude does not automatically apply event properties to future events, making them more complex to manage than user properties. See [Event properties](https://amplitude.com/docs/data/user-properties-and-events#event-properties) in the Amplitude documentation.
- **User properties** – Describes a user across all logged events. When set, Amplitude applies user properties to all subsequent recorded events until you update them. See [User properties](https://amplitude.com/docs/data/user-properties-and-events#user-properties) in the Amplitude documentation.

## Example decision notification listener

The following code examples use a [decision notification listener](docId:1SxhU55rO2wftlNek4f5U) to set an Amplitude user property and track an optional impression event. They send the flag key and variation key to Amplitude as a user property, so you can segment Amplitude reports, funnels, and cohorts by the flag variation each user saw.

:::hint{type="info"}
Register the decision notification listener when you initialize the SDK, before you call any decide methods. The listener does not forward decisions made before you register it.
:::

### Client-side SDKs

Client-side SDKs serve a single user per app instance. The Swift, Android, and JavaScript examples call the Amplitude client without a user ID, so Amplitude records the data against the user it is currently tracking. To correlate data between the two systems, set the Amplitude user ID to the same ID you pass to Feature Experimentation when you create the user context.

Select a language in the heading to view the code for that language. The Java and Kotlin examples are for the Android SDK, and the JavaScript example is for the browser.

```swift
import AmplitudeSwift
import Optimizely

// Initialize the Amplitude client
let amplitude = Amplitude(configuration: Configuration(apiKey: "<AMPLITUDE_API_KEY>"))

// Add a decision notification listener
let notificationId = optimizely.notificationCenter?.addDecisionNotificationListener { type, _, _, decisionInfo in
    guard type == "flag",
          decisionInfo["decisionEventDispatched"] as? Bool == true,
          let flagKey = decisionInfo["flagKey"] as? String,
          let variationKey = decisionInfo["variationKey"] as? String else {
        return
    }

    let propertyKey = "[Optimizely] \(flagKey)"

    // Set user property
    amplitude.identify(identify: Identify().set(property: propertyKey, value: variationKey))

    // Track impression event (optional)
    amplitude.track(eventType: "\(propertyKey) - \(variationKey)")
}
```

```java
import com.amplitude.android.Amplitude;
import com.amplitude.android.ConfigurationBuilder;
import com.amplitude.core.events.Identify;
import com.optimizely.ab.android.sdk.OptimizelyClient;
import java.util.Map;

// Initialize the Amplitude client
Amplitude amplitude = new Amplitude(
        new ConfigurationBuilder("<AMPLITUDE_API_KEY>", getApplicationContext()).build());

// Add a decision notification listener
OptimizelyClient optimizelyClient = optimizelyManager.getOptimizely();
int notificationId = optimizelyClient.addDecisionNotificationHandler(decisionNotification -> {
    Map<String, ?> decisionInfo = decisionNotification.getDecisionInfo();
    String variationKey = (String) decisionInfo.get("variationKey");
    if (!"flag".equals(decisionNotification.getType())
            || !Boolean.TRUE.equals(decisionInfo.get("decisionEventDispatched"))
            || variationKey == null) {
        return;
    }

    String propertyKey = "[Optimizely] " + decisionInfo.get("flagKey");

    // Set user property
    amplitude.identify(new Identify().set(propertyKey, variationKey));

    // Track impression event (optional)
    amplitude.track(propertyKey + " - " + variationKey);
});
```

```kotlin
import com.amplitude.android.Amplitude
import com.amplitude.android.Configuration
import com.amplitude.core.events.Identify

// Initialize the Amplitude client
val amplitude = Amplitude(
    Configuration(apiKey = "<AMPLITUDE_API_KEY>", context = applicationContext)
)

// Add a decision notification listener
val optimizelyClient = optimizelyManager.optimizely
val notificationId = optimizelyClient.addDecisionNotificationHandler { notification ->
    val decisionInfo = notification.decisionInfo
    val flagKey = decisionInfo["flagKey"] as? String
    val variationKey = decisionInfo["variationKey"] as? String
    if (notification.type == "flag" &&
        decisionInfo["decisionEventDispatched"] == true &&
        flagKey != null && variationKey != null
    ) {
        val propertyKey = "[Optimizely] $flagKey"

        // Set user property
        amplitude.identify(Identify().set(propertyKey, variationKey))

        // Track impression event (optional)
        amplitude.track("$propertyKey - $variationKey")
    }
}
```

```javascript
import {
  createPollingProjectConfigManager,
  createInstance,
  NOTIFICATION_TYPES
} from "@optimizely/optimizely-sdk";
import * as amplitude from '@amplitude/analytics-browser';

amplitude.init('<AMPLITUDE_API_KEY>');


const projectConfigManager = createPollingProjectConfigManager({
  sdkKey: "YOUR_SDK_KEY",
});

const optimizely = createInstance({
  projectConfigManager,
});


const onDecision = ({ type, decisionInfo }) => {
  const { decisionEventDispatched, variationKey, flagKey } =  decisionInfo;
  
  if (type === 'flag' && decisionEventDispatched) {
    const propertyKey = `[Optimizely] ${flagKey}`;
    const identify = new amplitude.Identify().set(propertyKey, variationKey);
    amplitude.identify(identify);

    // Track impression event (optional)
    amplitude.logEvent(`[Optimizely] ${flagKey} - ${variationKey}`);
  }
};

// Add a DECISION notification listener
const notificationId = optimizely.notificationCenter.addNotificationListener(
  NOTIFICATION_TYPES.DECISION,
  onDecision
);
```

```dart
import 'package:amplitude_flutter/amplitude.dart';
import 'package:amplitude_flutter/configuration.dart';
import 'package:amplitude_flutter/events/base_event.dart';
import 'package:amplitude_flutter/events/event_options.dart' as amp;
import 'package:amplitude_flutter/events/identify.dart';
import 'package:optimizely_flutter_sdk/optimizely_flutter_sdk.dart';

// Initialize the Amplitude client
final amplitude = Amplitude(Configuration(apiKey: '<AMPLITUDE_API_KEY>'));
await amplitude.isBuilt;

// Add a decision notification listener
final notificationId = await flutterSDK.addDecisionNotificationListener(
  (DecisionListenerResponse notification) {
    final decisionInfo = notification.decisionInfo;
    if (notification.type != 'flag' ||
        decisionInfo['decisionEventDispatched'] != true) {
      return;
    }

    final propertyKey = '[Optimizely] ${decisionInfo['flagKey']}';
    final variationKey = decisionInfo['variationKey'];
    final options = amp.EventOptions(userId: notification.userId);

    // Set user property
    amplitude.identify(Identify()..set(propertyKey, variationKey), options);

    // Track impression event (optional)
    amplitude.track(BaseEvent('$propertyKey - $variationKey'), options);
  },
);
```

The Android examples call `OptimizelyClient.addDecisionNotificationHandler` instead of `getNotificationCenter().addNotificationHandler`. The `getNotificationCenter()` method returns null before the SDK initializes, which causes a `NullPointerException`.

:::hint{type="info"}
The Android Java example uses `ConfigurationBuilder`, which requires version 1.33.0 or later of the Amplitude Android-Kotlin SDK (`com.amplitude:analytics-android`). For earlier versions, use the deprecated `new Configuration("<AMPLITUDE_API_KEY>", getApplicationContext())` constructor instead.
:::

:::hint{type="info"}
The Dart (Flutter) example passes the user ID from the notification to Amplitude. The `as amp` import prefix is required because the Optimizely Flutter SDK also exports a class named `EventOptions`.
:::

### Server-side SDKs

On a server, one listener handles decisions for every user. These examples read the user ID from the notification and pass it to Amplitude on each call, so Amplitude records the data against the same user ID that you pass to Feature Experimentation.

Select a language in the heading to view the code for that language. The Java example is for the Java SDK, and the JavaScript example is for Node.js.

```go
import (
    "github.com/amplitude/analytics-go/amplitude"
    "github.com/optimizely/go-sdk/v2/pkg/notification"
)

// Initialize the Amplitude client. Call ampClient.Shutdown() when your app exits.
ampClient := amplitude.NewClient(amplitude.NewConfig("<AMPLITUDE_API_KEY>"))

// Add a decision notification listener
notificationID, err := optimizelyClient.DecisionService.OnDecision(func(n notification.DecisionNotification) {
    if n.Type != notification.Flag {
        return
    }
    if dispatched, _ := n.DecisionInfo["decisionEventDispatched"].(bool); !dispatched {
        return
    }

    flagKey, _ := n.DecisionInfo["flagKey"].(string)
    variationKey, _ := n.DecisionInfo["variationKey"].(string)
    if variationKey == "" {
        return
    }

    propertyKey := "[Optimizely] " + flagKey
    options := amplitude.EventOptions{UserID: n.UserContext.ID}

    // Set user property
    identify := amplitude.Identify{}
    identify.Set(propertyKey, variationKey)
    ampClient.Identify(identify, options)

    // Track impression event (optional)
    ampClient.Track(amplitude.Event{
        EventType:    propertyKey + " - " + variationKey,
        EventOptions: options,
    })
})
if err != nil {
    // handle error
}
```

```java
import com.amplitude.Amplitude;
import com.amplitude.Event;
import com.optimizely.ab.notification.DecisionNotification;
import java.util.Map;
import org.json.JSONObject;

// Initialize the Amplitude client. Call amplitudeClient.shutdown() when your app exits.
Amplitude amplitudeClient = Amplitude.getInstance();
amplitudeClient.init("<AMPLITUDE_API_KEY>");

// Add a decision notification listener
int notificationId = optimizely.getNotificationCenter().addNotificationHandler(
    DecisionNotification.class,
    decisionNotification -> {
        Map<String, ?> decisionInfo = decisionNotification.getDecisionInfo();
        String variationKey = (String) decisionInfo.get("variationKey");
        if (!"flag".equals(decisionNotification.getType())
                || !Boolean.TRUE.equals(decisionInfo.get("decisionEventDispatched"))
                || variationKey == null) {
            return;
        }

        String userId = decisionNotification.getUserId();
        String propertyKey = "[Optimizely] " + decisionInfo.get("flagKey");

        // Set user property using the $identify event
        Event identifyEvent = new Event("$identify", userId);
        identifyEvent.userProperties = new JSONObject().put(propertyKey, variationKey);
        amplitudeClient.logEvent(identifyEvent);

        // Track impression event (optional)
        amplitudeClient.logEvent(new Event(propertyKey + " - " + variationKey, userId));
    });
```

```javascript
import { init, identify, track, Identify } from '@amplitude/analytics-node';
import {
  createInstance,
  createPollingProjectConfigManager,
  createBatchEventProcessor,
  NOTIFICATION_TYPES,
} from '@optimizely/optimizely-sdk';

// Initialize the Amplitude client
init('<AMPLITUDE_API_KEY>');

// Initialize the Optimizely client
const optimizely = createInstance({
  projectConfigManager: createPollingProjectConfigManager({ sdkKey: '<YOUR_SDK_KEY>' }),
  eventProcessor: createBatchEventProcessor(),
});

// Add a decision notification listener
const onDecision = ({ type, userId, decisionInfo }) => {
  const { decisionEventDispatched, flagKey, variationKey } = decisionInfo;
  if (type !== 'flag' || !decisionEventDispatched || !variationKey) {
    return;
  }

  const propertyKey = `[Optimizely] ${flagKey}`;

  // Set user property. The Amplitude Node.js SDK requires user_id to be at least 5 characters by default.
  const identifyObj = new Identify();
  identifyObj.set(propertyKey, variationKey);
  identify(identifyObj, { user_id: userId });

  // Track impression event (optional)
  track(`${propertyKey} - ${variationKey}`, undefined, { user_id: userId });
};

const notificationId = optimizely.notificationCenter.addNotificationListener(
  NOTIFICATION_TYPES.DECISION,
  onDecision
);
```

```python
from amplitude import Amplitude, BaseEvent, EventOptions, Identify
from optimizely.helpers import enums

# Initialize the Amplitude client. Call amplitude_client.shutdown() when your app exits.
amplitude_client = Amplitude("<AMPLITUDE_API_KEY>")

# Add a decision notification listener
def on_decision(decision_type, user_id, attributes, decision_info):
    if decision_type != enums.DecisionNotificationTypes.FLAG:
        return
    variation_key = decision_info.get("variation_key")
    if not decision_info.get("decision_event_dispatched") or variation_key is None:
        return

    property_key = f"[Optimizely] {decision_info['flag_key']}"

    # Set user property
    identify_obj = Identify()
    identify_obj.set(property_key, variation_key)
    amplitude_client.identify(identify_obj, EventOptions(user_id=user_id))

    # Track impression event (optional)
    amplitude_client.track(
        BaseEvent(event_type=f"{property_key} - {variation_key}", user_id=user_id)
    )

notification_id = optimizely_client.notification_center.add_notification_listener(
    enums.NotificationTypes.DECISION, on_decision
)
```

:::hint{type="info"}
The Python SDK uses snake\_case keys in `decision_info` (`flag_key`, `variation_key`, and `decision_event_dispatched`), unlike the other SDKs.
:::

### About `decisionEventDispatched`

Each decision notification includes a `decisionEventDispatched` field (`decision_event_dispatched` in the Python SDK). The field is `true` when the SDK sends a decision event to Optimizely for that decision, which appears as an impression on your experiment results. The examples on this page only forward these decisions, so your Amplitude data matches your Feature Experimentation results.

- **Experiments** – The field is `true` for every decision in an experiment, including when the user's variation has the flag turned off.
- **Targeted deliveries** – The field is `true` only when your project is configured to send flag decisions (the `sendFlagDecisions` value in the datafile).

### Summary of key elements

| Language                         | Decision callback                                    | Identify interface         |
| -------------------------------- | ---------------------------------------------------- | -------------------------- |
| Android (Java and Kotlin)        | Lambda passed to `addDecisionNotificationHandler`    | `Identify`                 |
| Dart (Flutter)                   | Callback passed to `addDecisionNotificationListener` | `Identify`                 |
| Go                               | Callback passed to `DecisionService.OnDecision`      | `amplitude.Identify`       |
| Java                             | `decisionNotification`                               | `Event` (`$identify` type) |
| JavaScript (browser and Node.js) | `onDecision`                                         | `Identify`                 |
| Python                           | `on_decision`                                        | `Identify`                 |
| Swift                            | Closure passed to `addDecisionNotificationListener`  | `Identify`                 |

If needed, log an impression event in the callback to signify that the Feature Experimentation test activated. Use these events to calculate conversion rates.

### Compare results

To compare Feature Experimentation results with Amplitude, apply a date filter in Amplitude that matches the experiment or targeted delivery dates. You can then filter Amplitude charts by the user property that the listener sets.

:::hint{type="info"}
User properties persist in Amplitude even when your Feature Experimentation test is not running.
:::

## Other SDKs

Amplitude does not publish an SDK that matches the following Feature Experimentation SDKs:

- Agent
- C#
- PHP
- Ruby

For these SDKs, send decision data directly to the [Amplitude HTTP V2 API](https://amplitude.com/docs/apis/analytics/http-v2). This API accepts JSON payloads and works with any server-side language.

1. Subscribe to decision notifications. In the C#, PHP, and Ruby SDKs, add a [decision notification listener](docId:1SxhU55rO2wftlNek4f5U). In Agent, enable notifications and subscribe to the `/v1/notifications/event-stream` endpoint.
2. For each decision, check that the type is `flag` and that the decision event was dispatched. Then read the user ID, flag key, and variation key from the notification.
3. Send a `POST` request to the Amplitude HTTP V2 API with the user ID and the flag and variation keys as user properties or event properties.
