Optimizely will be sunsetting Full Stack Experimentation on July 29, 2024. See the recommended Feature Experimentation migration timeline and documentation.

Dev GuideAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunitySumbit a ticketLog In
GitHubNuGetDev CommunitySumbit a ticket

Handle user IDs

This topic describes what User IDs are in Optimizely Full Stack.

User IDs are used to uniquely identify the participants in your experiments. Supply any string you want for user IDs, depending on your test design. UserIDs ensure that a single user is not randomly re-bucketed every time they see your experiment. In other words: a user always gets the same flag variation or flag on/off experience.

For example, if you are running tests on anonymous users, you can use a first-party cookie or device ID to identify each participant. If you are running tests on known users, you can use a universal user identifier (UUID) to identify each participant. If you are using UUIDs, you can run tests that span multiple devices or applications and ensure that users have a consistent treatment.

User IDs do not necessarily need to correspond to individual users. If you are running tests in a business application, you may want to pass account IDs to the SDK to ensure that every user in a given account has the same treatment. Alternatively, you can use bucketing IDs to ensure these users get a consistent experience.

Tips for using user IDs:

  • Ensure user IDs are unique: User IDs must be unique among the population you are using for tests. Optimizely buckets users and provides test metrics based on the user IDs that you provide.

  • Anonymize user IDs: The user IDs you provide are sent to Optimizely servers exactly as you provide them. You are responsible for anonymizing any personally identifiable data such as email addresses in accordance with our terms and other relevant regulations company's policies.

  • Use IDs from third-party platforms: If you are measuring the impact of your tests in a third-party analytics platform, we recommend leveraging the same user ID from that platform. This helps ensure data consistency and makes it easier to reconcile events between systems.

  • Use one namespace per project: Optimizely generally assumes a single namespace of user IDs for each project. If you are using multiple different conventions for user IDs in a single project (for example, anonymous visitor IDs for some tests and UUIDs for others), Optimizely will be unable to enforce rules such as mutual exclusivity between tests.

  • Use either logged-out or logged-in IDs: Optimizely does not currently provide a mechanism to alias logged-out IDs with logged-in IDs. If you are running tests that span both logged-out and logged-in states (for example, test on a signup funnel and track conversions after the user has logged in), you must persist logged-out IDs for the lifetime of the test.

  • Quickly conduct a vendor bake-off or test performance by using a request ID as a user ID. For an example of how Optimizely implements this approach, see Using experimentation to measure and validate backend performance improvements.

Assign variations with bucketing IDs

🚧

Important

Bucketing IDs are intended to support customers who want to assign variations with a different identifier than they use to count visitors. For example, a company might want to assign variations by account ID while counting visitors by user ID.

By default, Optimizely assigns users to variations (in other words, Optimizely buckets users) based on submitted user IDs. You can change this behavior by including a bucketing ID.

With a bucketing ID, you decouple user bucketing from user identification. Users who have the same bucketing ID are put into the same bucket and are exposed to the same variation. The bucketing ID serves as the seed of the hash value used to assign the user to a variation. Users with the same bucketing ID share the same hash value, which is why they are exposed to the same variation. For more information, see How bucketing works.

Bucketing IDs are implemented as a reserved attribute that you can use when activating an experiment or evaluating a feature flag. The example shows how to include a bucketing ID and send the $opt_bucketing_id attribute in the attributes parameter of the Activate, Is Feature Enabled, Get Feature Variable and Track methods.

import java.util.UUID;

import com.optimizely.ab.android.sdk.OptimizelyClient;
import com.optimizely.ab.config.Variation;

String experiementKey = "my_experiment";
// for simplicity sake, we are just using a generated user id
String userId = UUID.randomUUID().toString();

Map<String,String> attributes = new HashMap<String,String>();
attributes.put("ad_source", "my_campaign");
attributes.put("browser", "chrome");
// bucketing id can be passed in alone or with other attributes
attributes.put("$opt_bucketing_id", "bucketingId123");

// Activate with bucketing ID
Variation backgroundVariation = optimizely.activate(experiementKey, userId, attributes);

// Track with bucketing ID
// This tracks a conversion event for the event named `sample_conversion`
optimizely.track("sample_conversion", userId, attributes);

// GetVariation with bucketing ID
backgroundVariation = optimizely.getVariation(experimentKey, userId, attributes);
using OptimizelySDK;
using OptimizelySDK.Entity;

var optimizelyClient = new Optimizely(datafile);

var experimentKey = "my_experiment";
var userId = "user123";
UserAttributes attributes = new UserAttributes
{
  { "DEVICE", "iPhone" },
  { "AD_SOURCE", "my_campaign" },
  { "$opt_bucketing_id", "bucketingId123" }
};

// Activate with the bucketing ID
var variation = optimizelyClient.Activate(experimentKey, userId, attributes);

// Track with the bucketing ID
var eventKey = "my_conversion";
optimizelyClient.Track(eventKey, userId, attributes);

// Get variation with the bucketing ID
var variation2 = optimizelyClient.GetVariation(experimentKey, userId, attributes);
import java.util.UUID;

import com.optimizely.ab.Optimizely;
import com.optimizely.ab.config.Variation;

String experiementKey = "my_experiment";
// for simplicity sake, we are just using a generated user id
String userId = UUID.randomUUID().toString();

Map<String,String> attributes = new HashMap<String,String>();
attributes.put("ad_source", "my_campaign");
attributes.put("browser", "chrome");
// bucketing id can be passed in alone or with other attributes
attributes.put("$opt_bucketing_id", "bucketingId123");

// Activate with bucketing ID
Variation backgroundVariation = optimizelyClient.activate(experiementKey, userId, attributes);

// Track with bucketing ID
// This tracks a conversion event for the event named `sample_conversion`
optimizelyClient.track("sample_conversion", userId, attributes);

// GetVariation with bucketing ID
backgroundVariation = optimizelyClient.getVariation(experimentKey, userId, attributes);
var experimentKey = 'my_experiment';
var userId = 'user123';
var attributes = {
    'device': 'iphone',
    'ad_source': 'my_campaign',
    '$opt_bucketing_id': 'bucketingId123'
};

// Activate with the bucketing ID
var variationKey = optimizelyClient.activate(experimentKey, userId, attributes);

// Track with the bucketing ID
var eventkey = 'my_conversion';
optimizelyClient.track(eventKey, userId, attributes);

// Get variation with the bucketing ID
variationKey = optimizelyClient.getVariation(experimentKey, userId, attributes);
const bucketingIdAttributes = {
  device: "iphone",
  ad_source: "my_campaign",
  $opt_bucketing_id: "bucketingId123"
};

// Activate with the bucketing ID
var variationKey = optimizelyClient.activate(
  "my_experiment",
  userId,
  bucketingIdAttributes
);

// Track with the bucketing ID
var eventKey = "my_conversion";
optimizelyClient.track(eventKey, userId, bucketingIdAttributes);

// Get variation with the bucketing ID
variationKey = optimizelyClient.getVariation(
  "my_experiment",
  userId,
  bucketingIdAttributes
);
NSString *experimentKey = @"myExperiment";
NSString *userId = @"user123";
NSDictionary *attributes = @{@"device"              : @"iPhone",
                             @"ad_source"           : @"my_campaign",
                             @"$opt_bucketing_id"   : @"bucketingId123"};

// Activate with the bucketing ID
NSString *variationKey = [optimizely activateWithExperimentKey:experimentKey
                                                    userId:userId
                                                    attributes:attributes
                                                    error:nil];

// Track with the bucketing ID
NSString *eventKey = @"myEvent";
[optimizely trackWithEventKey:eventKey
                     		userId:userId
                       	attributes:attributes
                        error:nil];

// Get variation with the bucketing ID
NSString *variationKey = [optimizely getVariationKeyWithExperimentKey: experimentKey
                          userId:userId
                          attributes:attributes
                          error:nil];
$experimentKey = 'my_experiment';
$userId = 'user123';
$attributes = [
    'device' => 'iphone',
    'ad_source' => 'my_campaign',
    '$opt_bucketing_id' => 'bucketingId123'
];

// Activate with the bucketing ID
$variationKey = $optimizelyClient->activate($experimentKey, $userId, $attributes);

// Track with the bucketing ID
$eventKey = 'my_conversion';
$optimizelyClient->track($eventKey, $userId, $attributes);

// Get variation with the bucketing ID
$variationKey = $optimizelyClient->getVariation($experimentKey, $userId, $attributes);
experiment_key = 'app_redesign_attrib'
user_id = 'user123'
attributes = {
    'plan_type': 'silver',
    '$opt_bucketing_id': 'bucketingId123'
}

# Activate with the bucketing ID
variation = optimizely_client.activate(experiment_key, user_id, attributes)

# Track with the bucketing ID
event_key = 'purchased'
optimizely_client.track(event_key, user_id, attributes)

# Get variation with the bucketing ID
variation = optimizely_client.get_variation(experiment_key, user_id, attributes)
experiment_key = 'my_experiment'
user_id = 'user123'
attributes = {
    'device' => 'iphone',
    'ad_source' => 'my_campaign',
    '$opt_bucketing_id' => 'bucketingId123'
};

# Activate with the bucketing ID
variation_key = optimizely_client.activate(experiment_key, user_id, attributes)

# Track with the bucketing ID
event_key = 'my_conversion'
optimizely_client.track(event_key, user_id, attributes)

# Get variation with the bucketing ID
variation_key = optimizely_client.get_variation(experiment_key, user_id, attributes)
let experimentKey = "myExperiment"
let userId = "user123"
let forcedVariationKey = "treatment"
let attributes = ["device"           : "iPhone",
                  "ad_source"        : "my_campaign",
                  "$opt_bucketing_id": "bucketingId123"]

// Activate with the bucketing ID
let variationKey = try? optimizely.activate(experimentKey: experimentKey,
                                            userId: userId,
                                            attributes: attributes)

// Track with the bucketing ID
let eventKey = 'myConversion'
try? optimizely.track(eventKey: eventKey,
											userId: userId, 
                      attributes: attributes)

// Get variation with the bucketing ID
let variationKey = try? optimizely.getVariationKey(experimentKey: experimentKey,
                                                   userId: userId,
                                                   attributes: attributes)

Using a bucketing ID does not affect the user ID. Event data submissions will continue to include user IDs. With the exception of assigning users to specific variations, features that rely on user IDs behave the same regardless of the presence of a separate bucketing ID. If you do not pass a bucketing ID to the attributes parameter, users are bucketed by user IDs, which is the default method.

📘

Note

The bucketing ID is not persisted.

iOS 14 & Identifiers

Optimizely's SDKs do not access any user data or identifiers that are not explicitly passed in via instrumented API calls.

As a result, Optimizely does not have any direct dependency on or SDK changes planned to accommodate Apple's upcoming changes to its privacy disclosure and opt-in policy related to their ID for Advertisers (IDFA).

Optimizely does not link user data with third-party data for advertising or advertising measurement purposes or share data with data brokers. We recommend reviewing Apple's full requirements and guidance, other identifier options and your data use in detail to determine the correct path forward for your application's usage and compliance with disclosure requirements.

Users and billing

Starting September 2020, Optimizely offers monthly active users (MAU) pricing as an alternative to impressions. This includes a number of MAUs whose unique user IDs appear in calls to Optimizely's SDKs in a given month. Optimizely counts a monthly active user each time a decision (aka impression) or conversion event is sent for a unique user ID:

  • When the Activate or Is Feature Enabled is used.
  • When the Track method is used.

Users are counted even if they receive a disabled flag as a result of Activate or Is Feature Enabled because a decision event was made.

For more information, see What are monthly active users (MAUs) in Optimizely.

The MAU count includes unique anonymous IDs. If you use both Optimizely Web and Optimizely Full Stack, you can override anonymous Web user IDs with known Full Stack user IDs to avoid overcounting. For more information on including Web IDs, see our documentation on Bring your own ID (BYOID) in Optimizely Web.