---
title: "Segment"
description: "Report experiment exposures to Segment from your application, so the destinations you have connected receive the variation each user saw."
canonical_url: "https://docs.configdirector.com/integrations/analytics/segment"
---
# Segment

> Report experiment exposures to Segment from your application, so the destinations you have connected receive the variation each user saw.

## Introduction

To analyze an [experiment](https://docs.configdirector.com/getting-started/experiments) with data that goes through Segment, your application tells Segment which variation each user saw. You add a handler once, and Segment passes those exposures to your destinations along with the events you already send it.

Nothing passes through ConfigDirector, and ConfigDirector never holds your Segment write key.

## What Segment reads

Segment's [A/B testing spec](https://www.twilio.com/docs/segment/connections/spec/ab-testing) names one event, `Experiment Viewed`, with four properties:

<table>
<thead>
  <tr>
    <th>
      Property
    </th>
    
    <th>
      What you send
    </th>
    
    <th>
      Example
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        experiment_id
      </code>
    </td>
    
    <td>
      The config key
    </td>
    
    <td>
      <code>
        checkout-cta-copy
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        experiment_name
      </code>
    </td>
    
    <td>
      The config key
    </td>
    
    <td>
      <code>
        checkout-cta-copy
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        variation_id
      </code>
    </td>
    
    <td>
      The variation's ID, which stays the same when it is renamed
    </td>
    
    <td>
      <code>
        0190b5a2-7c1e-7f3a-9d44-2f6c8a1e5b70
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        variation_name
      </code>
    </td>
    
    <td>
      The variation's name
    </td>
    
    <td>
      <code>
        treatment-a
      </code>
    </td>
  </tr>
</tbody>
</table>

## Add the handler

Add a handler for the `configEvaluated` hook where you create the client, in the [browser](https://docs.configdirector.com/sdks/browser/javascript#hooks) or in [Node.js](https://docs.configdirector.com/sdks/server/node-js#hooks):

<code-group>

```ts [Browser]
import { createClient } from "@configdirector/client-sdk";
import { AnalyticsBrowser } from "@segment/analytics-next";

const analytics = AnalyticsBrowser.load({ writeKey: "YOUR-SEGMENT-WRITE-KEY" });

export const client = createClient("YOUR-CLIENT-SDK-KEY", {
  hooks: {
    configEvaluated: ({ evaluation }) => {
      const variation = evaluation.variation;
      if (!variation?.name) return;

      analytics.track("Experiment Viewed", {
        experiment_id: evaluation.key,
        experiment_name: evaluation.key,
        variation_id: variation.id,
        variation_name: variation.name,
      });
    },
  },
});

await client.initialize({ id: "user-42" });
```

```ts [Node.js]
import { createClient } from "@configdirector/server-sdk";
import { Analytics } from "@segment/analytics-node";

const analytics = new Analytics({ writeKey: "YOUR-SEGMENT-WRITE-KEY" });

export const client = createClient("YOUR-SERVER-SDK-KEY", {
  hooks: {
    configEvaluated: ({ evaluation }) => {
      const variation = evaluation.variation;
      const userId = evaluation.context?.id;
      if (!variation?.name || !userId) return;

      analytics.track({
        userId,
        event: "Experiment Viewed",
        properties: {
          experiment_id: evaluation.key,
          experiment_name: evaluation.key,
          variation_id: variation.id,
          variation_name: variation.name,
        },
      });
    },
  },
});

await client.initialize();
```

</code-group>

Only an experiment's evaluations carry a variation, so the check skips every other config, along with the in-code default value. It also skips a variation without a name, so [name each one](https://docs.configdirector.com/getting-started/experiments#create-the-experiment).

In the browser, Segment attaches the exposure to the user you have identified to it. In Node.js, the handler passes the context's `id` as the `userId`, so it has to be the ID your other Segment events use. A context without an `id` is skipped, since it does not take part in the split on a server.

The handler needs version 1.11.0 or later of the JavaScript SDK, or 1.13.0 or later of the Node.js SDK. React, Vue and React Native take the same handler in their `hooks`. The [Experiments guide](https://docs.configdirector.com/getting-started/experiments#send-exposures-to-your-analytics-tool) lists the hook for every other SDK.

## With OpenFeature

In an OpenFeature application, a hook does the same job, with the `variant` as the variation's name. Every config has a variant, not only experiments: on any other config it is ConfigDirector's ID for the value. So attach the hook to the reads of your experiments instead of registering it for every read. A hook does not see the variation's ID, so it sends the name in both properties:

<code-group>

```ts [Web]
import { OpenFeature, type Hook } from "@openfeature/web-sdk";
import { AnalyticsBrowser } from "@segment/analytics-next";

const analytics = AnalyticsBrowser.load({ writeKey: "YOUR-SEGMENT-WRITE-KEY" });

const reportExposure: Hook = {
  after: (hookContext, details) => {
    if (!details.variant) return;

    analytics.track("Experiment Viewed", {
      experiment_id: hookContext.flagKey,
      experiment_name: hookContext.flagKey,
      variation_id: details.variant,
      variation_name: details.variant,
    });
  },
};

const client = OpenFeature.getClient();

const buttonLabel = client.getStringValue("checkout-cta-copy", "Start free trial", {
  hooks: [reportExposure],
});
```

```ts [Node.js]
import { OpenFeature, type Hook } from "@openfeature/server-sdk";
import { Analytics } from "@segment/analytics-node";

const analytics = new Analytics({ writeKey: "YOUR-SEGMENT-WRITE-KEY" });

const reportExposure: Hook = {
  after: (hookContext, details) => {
    const userId = hookContext.context.targetingKey;
    if (!details.variant || !userId) return;

    analytics.track({
      userId,
      event: "Experiment Viewed",
      properties: {
        experiment_id: hookContext.flagKey,
        experiment_name: hookContext.flagKey,
        variation_id: details.variant,
        variation_name: details.variant,
      },
    });
  },
};

const client = OpenFeature.getClient();

const buttonLabel = await client.getStringValue(
  "checkout-cta-copy",
  "Start free trial",
  { targetingKey: "user-42" },
  { hooks: [reportExposure] },
);
```

</code-group>

A variation without a name also has ConfigDirector's ID for the value as its variant, so name each one. The hook needs version 1.11.0 or later of the [Web provider](https://docs.configdirector.com/sdks/openfeature/web#evaluation-details-and-hooks), or 1.13.0 or later of the [Node.js provider](https://docs.configdirector.com/sdks/openfeature/node-js#evaluation-details-and-hooks).

## When the handler runs

The handler runs once per evaluation, whenever your application reads the config. So read an experiment where the user reaches the change rather than at startup, or users who never see it count as exposed.

A config read on every render sends an event each time. Segment does not reduce them to one per user: it passes each one to your destinations, and each one [counts as an API call](https://www.twilio.com/docs/segment/guides/usage-and-billing/mtus-and-throughput) toward your plan's throughput. How a user is counted from there is up to each destination.

## Compare the variations

The outcomes you compare are the events your application already sends to Segment, so there is nothing more to add to your code.

Segment does not compare the variations itself. That happens in the destination you connect, so check which event its experiment feature reads. Some read only their own: Amplitude reads `$exposure`, which [you can send through Segment](https://amplitude.com/docs/feature-experiment/under-the-hood/event-tracking#exposure-event-definition) by using the event from the [Amplitude page](https://docs.configdirector.com/integrations/analytics/amplitude). Others can be pointed at this one: in Mixpanel, set the exposure event to `Experiment Viewed` under [**Settings** > **Project** > **Experimentation**](https://docs.mixpanel.com/docs/experiments#implementation-for-experimentation), with `experiment_name` and `variation_name` as its properties.

The handler also runs outside the split: before the run starts, after you ship and when a conditional rule serves a variation. So limit your analysis to the dates of the run, which the [**Experiment** tab](https://docs.configdirector.com/getting-started/experiments#read-the-exposures) lists. A conditional rule above the split can still make the numbers higher than the **Exposures** card, which counts only the contexts the split served.

Keep each variation's name for the length of the run if your destination compares by `variation_name`, because a new name would show up there as a new variation.

Chart markers from an [analytics integration](https://docs.configdirector.com/integrations/analytics-integrations) are not offered for this tool; they are for Amplitude, Mixpanel, Datadog, Grafana and PostHog.


## Sitemap

See the full [sitemap](https://docs.configdirector.com/sitemap.md) for all pages.
