# Amplitude Documentation — Full Corpus
# Generated: 2026-07-11T21:20:15Z
# Pages: 815
# Source: https://docs.statsig.com/llms.txt
================================================================================
# Statsig Overview
URL: https://docs.statsig.com/welcome
Updated: 2026-07-11
================================================================================
Statsig is a unified platform for feature flags, A/B testing, and product analytics. Ship, measure, and learn with tools used by leading tech companies.
## Quick links
{% card-grid columns="3" %}
{% card title="SDK Quickstart" href="/sdks/quickstart" icon="bolt" %}
Install the Statsig SDK and get started in minutes.
{% /card %}
{% card title="Intro to Statsig" href="/understanding-platform" icon="flag-checkered" %}
Learn more about core concepts and features of Statsig.
{% /card %}
{% card title="Warehouse Native" href="/statsig-warehouse-native/introduction" icon="database" %}
Read about how to use Statsig with your data warehouse.
{% /card %}
{% /card-grid %}
## Key features
{% card-grid columns="3" %}
{% card title="Feature Flags" href="/feature-flags/overview" img="images/homepage/flags.png" %}
Control feature rollouts and manage deployments safely with feature flags.
{% /card %}
{% card title="Experimentation" href="/experiments/overview" img="images/homepage/pulse.png" %}
Run A/B tests and experiments to optimize your product decisions.
{% /card %}
{% card title="Product Analytics" href="/product-analytics/overview" img="images/homepage/mex.png" %}
Analyze user behavior and product metrics with powerful analytics tools.
{% /card %}
{% /card-grid %}
You can use each of these features on top of your own data warehouse through [Warehouse Native](/statsig-warehouse-native/introduction) with no ETL, or host your data in Statsig's infrastructure.
***
## Explore SDKs
Statsig offers SDKs for a wide variety of platforms to suit any codebase or deployment preference:
### Client SDKs
{% card-grid columns="3" %}
{% card title="JavaScript" href="/client/javascript-sdk" icon="js" %}
Browser JavaScript
{% /card %}
{% card title="React" href="/client/React" icon="react" %}
Client-Side React
{% /card %}
{% card title="React Native" href="/client/ReactNative" icon="react" %}
Bare React Native SDK
{% /card %}
{% card title="Next.js" href="/client/Next" icon="n" %}
Next.js SSR, SSG & Client-Side
{% /card %}
{% card title="Angular" href="/client/Angular" icon="angular" %}
Angular bindings for Javascript SDK
{% /card %}
{% card title="Swift" href="/client/iosClientSDK" icon="swift" %}
iOS, MacOS, tvOS SDK
{% /card %}
{% card title="Android" href="/client/Android" icon="android" %}
Android Kotlin/Java SDK
{% /card %}
{% card title=".NET Client" href="/client/DotNet" icon="dotnet" %}
Client SDK for .NET framework
{% /card %}
{% card title="Roku" href="/client/Roku" icon="roku" %}
Roku Brightscript SDK
{% /card %}
{% card title="Unity" href="/client/Unity" icon="unity" %}
Unity game engine SDK
{% /card %}
{% card title="Dart/Flutter" href="/client/Dart" icon="flutter" %}
Flutter/Dart Mobile App SDK
{% /card %}
{% card title="C++ Client" href="/client/CPP" icon="cplusplus" %}
C++ client-side SDK
{% /card %}
{% /card-grid %}
### Server side SDKs
{% card-grid columns="3" %}
{% card title="Node.js" href="/server-core/node-core" icon="node-js" %}
Node.js server SDK
{% /card %}
{% card title="Java" href="/server-core/java-core" icon="java" %}
Java server SDK
{% /card %}
{% card title="Python" href="/server-core/python-core" icon="python" %}
Python server SDK
{% /card %}
{% card title="Go" href="/server/go" icon="golang" %}
Go server SDK
{% /card %}
{% card title="Ruby" href="/server/ruby" icon="ruby" %}
Ruby server SDK
{% /card %}
{% card title=".NET Server" href="/server-core/dotnet-core" icon="dotnet" %}
.NET server SDK
{% /card %}
{% card title="PHP" href="/server-core/php-core" icon="php" %}
PHP server SDK
{% /card %}
{% card title="Rust" href="/server-core/rust-core" icon="rust" %}
Rust server SDK
{% /card %}
{% card title="C++ Server" href="/server-core/cpp-core" icon="cplusplus" %}
C++ server SDK
{% /card %}
{% /card-grid %}
### Integrations
{% card-grid columns="3" %}
{% card title="Webflow" href="/guides/webflow-sidecar-ab-test" icon="w" %}
Webflow integration
{% /card %}
{% card title="Shopify" href="/guides/shopify-ab-test" icon="shopify" %}
Shopify integration
{% /card %}
{% card title="Segment" href="/integrations/data-connectors/segment" icon="segment" %}
Segment data connector
{% /card %}
{% card title="Rudderstack" href="/integrations/data-connectors/rudderstack" icon="rudderstack" %}
Rudderstack connector
{% /card %}
{% card title="Hightouch" href="/integrations/data-connectors/hightouch" icon="hightouch" %}
Hightouch integration
{% /card %}
{% card title="mParticle" href="/integrations/data-connectors/mparticle" icon="mparticle" %}
mParticle connector
{% /card %}
{% card title="Framer" href="/guides/framer-analytics" icon="f" %}
Framer integration
{% /card %}
{% card title="Slack" href="/integrations/slack" icon="slack" %}
Slack notifications
{% /card %}
{% card title="Integrations" href="/integrations/introduction" icon="puzzle" %}
View more integrations
{% /card %}
{% /card-grid %}
If you don't see the SDK or framework you need, reach out directly in the [Slack Community](https://statsig.com/slack).
***
## Learning resources
* **Statsig University**: A comprehensive collection of onboarding and training content, including a video Resource Library and a schedule of user training Webinars.
* **Community**: Join the Slack Community to connect with other users and get help from the Statsig team.
* **Blog**: Stay updated with the latest developments in product experimentation and feature management on the [Blog](https://statsig.com/blog).
* **Use Cases**: Discover [Customer Stories](https://statsig.com/customers) to learn why customers love Statsig and how they use it to power their product development.
================================================================================
# Platform Overview
URL: https://docs.statsig.com/understanding-platform
Updated: 2026-07-11
================================================================================
## What Statsig is used for
Statsig's goal is to be the single platform to ship, measure, and learn from the products you build. The most popular features of Statsig are:
* [Feature Flags](/feature-flags/overview): Expose new features to select user groups, roll them out (and roll them back, when things go wrong), and measure the impact they have.
* [Experiments](/experiments/overview): Run randomized, controlled experiments on different variations of your product, and measure the exact impact on your users. Customers use Statsig to run thousands of experiments each year, and iterate in the right direction.
* [Product Analytics](/product-analytics/overview): Understand the trends of your core business metrics, user behavior, and more.
* [Infra Analytics](/infra-analytics/overview): Monitor and debug service health alongside product outcomes. Ingest metrics and traces with OpenTelemetry, search and group logs, and set alerts to catch regressions fast.
Statsig also offers other tools like [Session Replay](/session-replay/overview) and [Web Analytics](/webanalytics/overview/), and these features are most effective when used together.
***
## How to set up Statsig
The most common setup approach is to integrate [Statsig's SDKs](/sdks/quickstart), which let you integrate flags and experiments, and track your core business metrics. If you want to bring existing data to Statsig, you have a few options:
* **I'd like to bring existing data from my Data Warehouse:** You'll likely want to use [Statsig Warehouse Native](/statsig-warehouse-native/introduction), a zero-ETL model for running experiments and product analytics on top of your existing data. Alternatively, you can import warehouse data into Statsig Cloud with the [warehouse ingestions](/data-warehouse-ingestion/introduction).
* **I'd like to bring existing data from my Segment, Rudderstack, Amplitude, or another platform:** Consider one of Statsig's [integrations](/integrations/introduction), which can port your events straight into Statsig Cloud.
* **I'd like to import my existing experiment assignment data, and use Statsig for Analysis:** You'll need to use [Statsig Warehouse Native](/statsig-warehouse-native/introduction).
Statsig has two deployment models based on your needs: Statsig Cloud (where Statsig hosts your data) and Statsig Warehouse Native (where you host your data in your own warehouse).
***
## Statsig Cloud
With Statsig Cloud, install the Statsig SDK and configure event logging. Statsig handles everything else.
* You get feature flags and 2 million metered events for free.
* Includes analytics tools such as Dashboards, Metrics Explorer, and Insights.
* For pricing details, refer to the [pricing page](https://www.statsig.com/pricing).
Statsig Cloud is a good option for teams that want to get started quickly without managing infrastructure or data warehousing.
***
## Statsig Warehouse Native (WHN)
If your events and metrics already reside in your own data warehouse and you have a dedicated data team, Statsig Warehouse Native (WHN) may be a better option.
* WHN lets you host Statsig's Stats Engine within your warehouse, enabling you to calculate metric lifts on your pre-existing datasets.
* You can choose between two methods:
1. **Using 3rd party or your own SDKs**: You handle feature assignment and provide exposure data (you randomize the users).
2. **Using Statsig SDKs**: Statsig handles randomization and writes data into your warehouse.
The first method helps you scale analysis, while the second can significantly increase your experimentation velocity.
{% callout type="note" %}
WHN is available only with Enterprise contracts. If you're interested in this option, go to [Statsig Warehouse Native](/statsig-warehouse-native/introduction) or [schedule a demo](https://www.statsig.com/contact/demo) with the Sales team.
{% /callout %}
***
## Choosing between Cloud and Warehouse Native
The following criteria can help you decide between the two deployment modes:
| Criteria | Cloud-hosted | Warehouse native (WHN) |
| --- | --- | --- |
| Data source | Primary source of metrics comes from Statsig SDKs or CDPs like Segment. Some metrics can still come from a warehouse. | Warehouse is the primary source of metrics, making WHN ideal when you want to reuse existing data pipelines and computation. |
| Analysis needs | Automated experimentation for every experiment and product launch, especially with metrics derived from event logging. | Flexible analysis on top of your existing source of truth metric data. |
| Data team involvement | Optional but recommended for experiment design and readouts. | Necessary for setting up the warehouse connection and configuring core metrics, but not mandatory for every experiment. |
| Costs | TCO is slightly lower. No warehouse costs involved. | TCO includes Statsig license + costs incurred for computation and storage in your warehouse. |
| Modularity | An integrated end-to-end platform that spans SDKs for feature rollout, experiment execution, analysis, and experiment readouts. | Modular: You can opt for the integrated end-to-end platform or choose to use only a subset of capabilities, such as assignment or experiment analysis. |
For a detailed comparison, go to [Statsig Cloud vs Warehouse Native](https://www.statsig.com/blog/deciding-cloud-hosted-versus-warehouse-native-experimentation-platforms).
## Get started with Statsig
After you decide which deployment model fits your needs, choose the appropriate getting started guide:
* [Getting Started with Statsig Cloud](/sdks/getting-started)
* [Getting Started with Statsig Warehouse Native](/statsig-warehouse-native/guides/quick-start)
{% callout type="info" %}
Have a question or need help? The Statsig Engineering, Data, and Product teams are available in the [Slack community](https://www.statsig.com/slack).
{% /callout %}
================================================================================
# Support Options
URL: https://docs.statsig.com/support-options
Updated: 2026-07-11
================================================================================
Statsig uses automated tooling in the Slack Connect channel to triage and prioritize inbound support messages. All support at Statsig is handled by actual, full-time engineers.
While Statsig maintains a monitored support email, Slack is strongly encouraged as the primary channel for support and communication to ensure the fastest response times.
Preferred methods of raising a support request:
1. Slack message through your dedicated Slack channel or [Slack Community](https://statsig.com/slack)
2. Email to support@statsig.com is discouraged but available for sensitive topics
## Community vs Standard vs Premium support
| Feature | Community | Standard | Premium |
|---------|-----------|----------|---------|
| Access to Statsig University [certifications](https://learn.statsig.com/certifications) and [webinar](https://learn.statsig.com/pages/upcoming-webinars) training sessions | Yes | Yes | Yes |
| Access to Statsig's global [Slack community](https://statsig.com/slack) for best practices and peer insights | Yes | Yes | Yes |
| Basic assistance during Statsig business hours | Yes | Yes | Yes |
| Customer member access to Private Slack channel with Statsig(1) | No | No | Up to 4 members |
| Critical/High Priority support with 4 hour response time during Statsig business hours(2) | No | No | Yes |
| 24 hour response time for all other inquiries during Statsig business hours | No | No | Yes |
| Dedicated Account Manager to support ongoing needs | No | Yes | Yes |
| Check-ins with the Statsig team experts to align on goals and progress | No | As needed | Quarterly |
| Data science consultations for advisory and experiment reviews | No | No | Up to 4 |
| Early access invitation to events hosted by Statsig and partners | No | No | Yes |
1. Standard or Premium support is only available for [Enterprise plans](https://www.statsig.com/pricing).
2. Statsig standard business hours of 8am-5pm PT.
## Learning resources
* **Statsig University**: A comprehensive collection of onboarding and training content, including a video [Resource Library](https://learn.statsig.com/pages/resource-library) and a schedule of user training [Webinars](https://learn.statsig.com/pages/upcoming-webinars).
* **Statsig Status Page**: Monitor Statsig's operational status and configure [Slack alerts](/integrations/slack) to be notified of updates.
* **Community**: Join the [Slack Community](https://statsig.com/slack) to connect with other users and get help from the Statsig team.
* **Blog**: Stay updated with the latest developments in product experimentation and feature management on the [Blog](https://statsig.com/blog).
* **Use Cases**: Discover [Customer Stories](https://statsig.com/customers) to learn why customers love Statsig and how they use it to power their product development.
================================================================================
# Experiments Overview
URL: https://docs.statsig.com/experiments/overview
Updated: 2026-07-11
================================================================================
Experimentation is a tool for making data-driven decisions that improve product outcomes and customer experiences.
This guide covers key concepts of experimentation: control variables, randomization units, and statistical significance. It helps you understand the science behind A/B testing and multivariate experiments and how to use experiments to validate product changes, discover new opportunities, and drive business impact.
{% figure %}

{% /figure %}
## What are experiments
Experiments enable you to run randomized controlled trials (A/B or A/B/n tests) to measure the impact of product changes on key metrics. Statsig’s experimentation platform is designed to make it straightforward to create, manage, and analyze experiments, ensuring you ship features that deliver value to your users and business.
Experiments are ideal when you want to:
* Test multiple variants (A/B or A/B/n) of a product feature.
* Run mutually exclusive experiments in parallel.
* Measure the direct impact of changes on product and business metrics.
***
## Why experiment
Controlled experiments are the most scientifically reliable way to establish causality between your product changes and their effect on customer behavior. By running experiments, you can:
* **Validate Hypotheses**: Only ship features that have been proven to improve the customer experience or drive key business metrics.
* **Measure Success**: Measure feature performance post-launch and detect any unexpected side effects.
* **Drive Innovation**: Experiments give teams real-time feedback on product performance, enabling faster iteration and better, data-driven decisions.
Historical metrics may show correlation, but experiments allow you to establish causal relationships. Experiments reduce the influence of uncontrolled external factors, ensuring that observed effects are due to the tested changes.
***
## Key concepts
### Control variables
A **control variable** is the variable in an experiment that is manipulated to observe its effect on key metrics. In a simple A/B test, the control variable usually has two values (A and B). More complex experiments may have additional values (e.g., A, B, C, D), known as multivariate experiments.
### Variants
A **variant** is a specific version of the product or feature being tested. For example, in an A/B test:
* **A (Control)**: Represents the current state of the product or feature.
* **B (Treatment)**: Represents the modified state you want to evaluate.
Each variant is randomly assigned to users, allowing you to compare their performance.
### Randomization unit
The **randomization unit** is the entity (such as a user, device, or session) that is randomly assigned to control or treatment groups in an experiment. Choosing the right randomization unit ensures consistency in user experience and reliable experiment results.
This choice is critical to ensure that experiment results reflect real-world user behavior and that data isn't skewed by unintentional crossovers between groups.
### Statistical significance
Statistical significance determines whether the observed changes in metrics are likely due to the product change or random variation. Two commonly used methods are:
* **p-value**: The p-value measures the probability of observing the results by chance if the variant had no effect. A p-value below 0.05 is typically used to determine statistical significance.
* **Confidence Interval**: A confidence interval defines the range in which the true effect of a variant lies, with a given level of confidence (e.g., 95%). If the confidence interval doesn't overlap zero, the effect is considered statistically significant.
For more information on designing, monitoring, and analyzing experiments, refer to [Product Experimentation Best Practices](https://statsig.com/blog/product-experimentation-best-practices).
***
## Common scenarios for experimentation
### Optimize product growth
Use experiments to refine and optimize user experiences, helping you climb toward a local maximum in your product strategy. Common goals include:
* Optimizing a specific user journey (e.g., improving onboarding).
* Iterating on features to identify high-return opportunities.
* Aligning experiments with business-critical metrics and guardrails to prevent negative side effects on fundamental business needs.
### Explore new opportunities
Use exploratory experiments to discover entirely new directions. These experiments help you develop new ideas, validate strategies, and uncover long-term opportunities.
* Run experiments over longer durations to account for novelty effects and adoption time.
* Slowly ramp up experiments to minimize risk and build statistical power.
* Test multiple related hypotheses to explore a broader business strategy.
***
## Choosing the right randomization unit
The Randomization Unit is the variable that determines how users are distributed across your groups (for example, test and control). When you set a variable as the Randomization Unit, any value for that variable always receives the same experience. The Randomization Unit is also the reference unit for your metrics. If you choose userID, the userID deterministically buckets each user and serves as the basis of measurement: your analysis might look at Revenue per userID. The following are common units of randomization and when to use them.
### User identifiers
The most commonly used randomization unit is the **User ID**. Your application generates this identifier when a user registers or signs in. Using User IDs ensures a consistent user experience across sessions and devices, because the user is always assigned the same variant regardless of where or when they access the product.
Advantages:
* Persistent across sessions and devices.
* Independent of client-side cookies, which can be cleared by users.
For more details on using User IDs with Statsig, refer to [Statsig Docs on User Identifiers](/sdks/user).
### Device identifiers
For users who haven't registered or signed in, using **Device IDs** or **Anonymous User IDs** is common. These identifiers track users based on their device and are ideal when experimenting with unregistered or guest users.
Example:
* You can use device IDs to experiment on landing page optimizations aimed at improving user registration rates.
**Drawbacks**:
* Device-specific: If the same user accesses your app from multiple devices, they may have different experiences.
* Shared devices: If multiple users share a device, the experiment may mistakenly treat their behavior as belonging to one individual.
Statsig SDKs automatically generate **Stable IDs** for anonymous users, making it easier to manage device-based experiments. For more details, refer to [Statsig Guide for Device-Level Experiments](/guides/first-device-level-experiment).
### Session identifiers
In certain cases, you may use **Session IDs** as the unit of randomization, particularly when testing behavior during a single session (for example, optimizing a checkout flow). Session-based randomization assumes each session is independent of others. This assumption may not hold if users return in multiple sessions.
Example:
* Session IDs might be used when experimenting with conversion funnels for guest checkouts, which are typically completed within a single session.
**Drawbacks**:
* Users may remember their experience from one session to another, undermining the assumption of session independence.
* If users return in future sessions, they may be placed in different variants, leading to inconsistent user experiences.
***
## Tutorials
* [Run your first A/B test](/guides/abn-tests)
* [Create an experiment using a userID](/experiments/create-new)
* [Create an experiment using a customID](/guides/experiment-on-custom-id-types)
* [Use a language specific Statsig SDK to implement an experiment in your application](/experiments/implementation/implement)
* [Monitor an experiment](/experiments/monitor)
================================================================================
# Create an Experiment
URL: https://docs.statsig.com/experiments/create-new
Updated: 2026-07-11
================================================================================
{% callout type="tip" %}
Metrics and experiments behave differently in Warehouse Native. Read about [Configuring Experiments in Warehouse Native](/statsig-warehouse-native/features/experiment-options).
{% /callout %}
This doc walks through the steps of creating a new experiment in the Statsig console. For an end-to-end guide that includes integrating the Statsig SDK, go to [Run your first experiment](/guides/abn-tests).
## User-level experiments
To create a user-level experiment, follow these steps:
1. Log into the Statsig console at [https://console.statsig.com/](https://console.statsig.com/)
2. Navigate to **Experiments** in the left-hand navigation panel
3. Click on the **Create** button
4. Enter the name and description for your experiment as shown in the figure below
5. By default, your experiment runs in its own **Layer**. A Layer allows you to manage multiple experiments and feature flags together. To add this experiment to an existing Layer, select **Add Layer** under **Advanced** in the experiment creation modal. To create a new Layer, select **Create New Layer**.
6. Click **Create**
{% figure %}

{% /figure %}
## Configure your scorecard
When running an experiment, it’s common to test a specific hypothesis using a set of key metrics. The **Scorecard** feature makes this easy by letting you enter your hypothesis and select both primary and secondary metrics.
* **Primary Metrics** are those you expect to be directly impacted by the experiment.
* **Secondary Metrics** are important to monitor to ensure there are no unintended side effects, but they aren’t the primary focus of your experiment.
Configuring the Scorecard is a required step when creating an experiment. It provides your team with clear context on what is being tested and how success is measured. You must enter your hypothesis and select at least one primary metric. Metrics added to the Scorecard are computed daily and eligible for advanced treatments like [CUPED](/experiments/statistical-methods/methodologies/cuped) and [Sequential Testing](/experiments/advanced-setup/sequential-testing).
For best practices on configuring your Scorecard, go to [Read Results](/experiments/interpreting-results/read-results).
{% figure %}

{% /figure %}
## Configure allocation and targeting
The Allocation and Targeting section is where most of your experiment configuration happens.
### Allocation
For **Allocation**, enter the percentage of users you want to assign to this experiment. You can allocate up to 100% of eligible users. It’s good practice to start with a smaller percentage, verify the experiment’s stability, and then increase the allocation.
{% figure %}

{% /figure %}
You can increase the allocation of your experiment anywhere from 0% to 100% at any time after experiment start; however, you can't decrease allocation without resetting your experiment, as this would cause biases in group allocation and pollute your metric results.
### Targeting
To configure **Targeting** criteria, click to edit the **Targeting** section. You can either set new targeting criteria or use an existing **Feature Gate**. This limits the experiment to only the users who meet the defined conditions.
{% figure %}

{% /figure %}
* For basic targeting criteria, creating it through Inline Targeting works well. (Select "Criteria: Everyone" to get started.)
* For more advanced targeting (for example, progressive rollouts) or if you want to maintain targeting criteria when you launch your experiment, reference an existing **Feature Gate**.
By default, no targeting criteria are set, so your experiment includes all allocated users within the defined **Layer** or exposed user base.
## Configure your groups and parameters
When configuring **Groups and Parameters**, define your parameters first. These are the variables that control the behavior of the different experiment variants.
* Enter the values the experiment parameter takes for each variant. For more about the difference between **Groups** and **Parameters**, go to [Groups vs. Parameters](/experiments/implementation/getting-group).
You can add additional groups by clicking the "+" next to the existing groups. The user allocation automatically adjusts as you add more groups.
{% figure %}

{% /figure %}
You can also name, describe, and add variant images for each group under the **Groups** section. Only the parameters and values affect what users see; group names and descriptions aren't used in the experiment code.
## Device-level and custom ID experiments
By default, experiments randomize users based on **User ID**. If you need to use a different ID type (for example, device-level), follow steps 1–4 from the "User-level Experiments" section, then:
1. Click the **ID Type** dropdown menu and choose the ID type you need.
2. Click **Create**
{% figure %}

{% /figure %}
Afterward, continue with the same steps described above to finish configuring the experiment.
## ID mapping capabilities
When running experiments, you may want to start with one ID type (like stableID for device-level targeting) but analyze results using events from another ID type (like userID for logged-in user metrics).
**Warehouse Native**: Supports ID mapping between different identifier types (for example, stableID to userID) through Entity Property Source configuration.
**Cloud**: Does not currently support mapping between different ID types. Experiments started with stableID only analyze events with stableID, and experiments started with userID only analyze events with userID.
For advanced ID mapping requirements, consider using Statsig Warehouse Native.
## Isolated experiments
If you want to create an experiment that excludes users exposed to other experiments, follow steps 1–4 from the "User-level Experiments" section. Then:
1. Select **Advanced** options.
2. Select an existing **Layer** or create a new one.
3. Click **Create**.
{% figure %}

{% /figure %}
Now, complete the rest of the experiment setup as described above.
## Reusing experiment salts
The Statsig SDKs use [deterministic hashing](/sdks/how-evaluation-works) to bucket users. The same user evaluated for the same experiment is bucketed identically, regardless of where evaluation occurs. Every experiment has its own unique salt so that each experiment's assignment is random.
For advanced use cases (for example, a series of related experiments that need to reuse the control and test buckets), Statsig exposes the ability to copy and set the salts used for deterministic hashing. This feature is intended for careful use and is available only to Project Administrators. It is available in the Overflow (...) menu in Experiments.
## Significance level adjustments
By default, Experiment Results display with 95% confidence intervals and without Bonferroni correction. You can customize this during experiment setup or later when viewing results in Experiment Results.
* **Bonferroni Correction:** Apply this to reduce the risk of false positives in experiments with multiple test groups. The significance level (*α*) is divided by the number of test variants.
* **Default Confidence Interval:** Choose a lower confidence interval (for example, 80%) if you prefer faster results with higher tolerance for false positives, or use 95% for greater certainty.
{% figure %}

{% /figure %}
## Target duration
Setting a target duration is optional, but it helps ensure that you wait long enough for the experiment to reach full power. You can set the target as either a specific number of days or a number of exposures, and use the [**Power Analysis Calculator**](/experiments/power-analysis) to determine what target works best for your metrics.
{% figure %}

{% /figure %}
**Target durations longer than 90 days:** By default, Statsig computes Experiment Results for the first 90 days, though the experiment itself can run longer. Before setting a duration beyond 90 days, consider whether results past that period will still be relevant, and whether earlier data might already provide the insights you need.
{% figure %}

{% /figure %}
After you set the target, you can track progress against the target duration/exposures in the experiment header. You also receive notifications through email and Slack (if integrated) when the target is reached.
***
## Hypothesis Advisor
Writing good experiment hypotheses is key to a strong experimentation culture. Statsig gives instant feedback on experiment hypotheses, flagging what’s missing. Admins can set custom requirements, which Statsig uses to guide experimenters toward stronger, more complete hypotheses.
This Statsig AI feature is disabled by default. Enable it from *Settings > Experiment > Project > Statsig AI*.
This is also where you configure any custom requirements you want Hypothesis Advisor to enforce (for example, "Strongly recommend that a validation plan be mentioned").
{% figure %}

{% /figure %}
================================================================================
# Power Analysis
URL: https://docs.statsig.com/experiments/power-analysis
Updated: 2026-07-11
================================================================================
## What is power analysis
The Statsig Power Analysis Calculator helps determine the experiment parameters needed to reach a statistically significant result before an experiment runs. The most common variable to optimize is duration (how long your experiment runs), but the power calculator supports other variables as well. Using the known mean and variance of a metric and the observed traffic volume, the Power Analysis Calculator estimates the relationship between three variables:
- **Minimum detectable effect (MDE)**: The smallest change in the metric that the experiment can reliably detect. For example: an MDE of 1% with Power set to 80% means that if there's a true effect of 1% on the metric, the experiment has an 80% chance to produce a statistically significant result. If the magnitude of the true effect is smaller than 1%, the experiment is less likely to produce a statistically significant result (though it can still occur).
- **Number of days or exposures**: How long the experiment is active and the number of users enrolled in it. Longer running experiments typically have more observations, leading to tighter confidence intervals and smaller MDE. We use historical data to estimate the number of new users that would be eligible for the experiment each day.
- **Allocation**: The percentage of traffic that participates in the experiment. Larger allocation leads to smaller MDE, so allocating as many users as possible is often preferable for faster or more sensitive results. When there's a risk of negative impact or a need for mutually exclusive experiments, it's useful to know the smallest allocation that can achieve your target MDE.
Many experimentation practitioners use the Power Analysis Calculator during setup of every experiment.
## Using the power analysis calculator
The Power Analysis Calculator is accessible from the tools menu. It is also linked on the experiment setup page, below the Experiment Duration field.
{% figure %}

{% /figure %}
1. Select the population used to determine the metric mean and variance and to estimate the number of exposures over time.
- **Everyone**: Analysis is based on the entire user base.
- **Targeting gate**: Analysis is scoped to the set of users who pass the selected feature gate, which must have been active for at least 7 days. Choose this option when you plan to use a targeting gate for the experiment.
- **Past experiment**: Analysis is based on data collected from a previous experiment. Use this option when the new experiment impacts a similar user base or part of the product as the previous one.
- **Qualifying event**: Analysis is scoped to the set of users who logged the event specified.
2. Select a metric of interest (or multiple metrics for a targeting gate analysis)
3. Click on **Run Power Analysis** to calculate results
{% figure %}

{% /figure %}
Your past power analysis calculations are available in the "Past Analyses" tab.
{% figure %}

{% /figure %}
To attach an existing power analysis to an experiment, use the dropdown menu on an existing power analysis.
{% figure %}

{% /figure %}
## Population types
The population selected directly determines the inputs of the analysis (mean, variance, number of users). To obtain reliable power analysis estimates, the metric values of the selected population should roughly match those of the users you plan to target in the experiment.
### Example
Suppose you want to test a change in the checkout flow and determine your expected MDE for total_purchases. If only approximately 10% of your daily users reach the checkout page and you use the _Everyone_ population for your analysis, you are likely to:
- Overestimate the number of users the experiment gets.
- Underestimate the mean value of the total_purchases metric. The 90% of users that don't reach the checkout page have a value of zero, but in practice they won't be in the experiment and won't contribute to the metric.
- Incorrectly estimate the variance in the total_purchases metric. The distribution of metric values changes when you include the 90% of users with zero purchases because they never reached the checkout page.
When an experiment includes only a biased subset of users, the MDE and duration from the power analysis may not be a reliable estimate.
One way to address this is to use data from a past experiment to estimate the power of a new, similar experiment. For example, if a prior experiment also targeted the checkout page, that data provides better estimates of traffic volumes and metrics for that part of the product.
### Inputs by population type
The following table shows how inputs for the power analysis are obtained from the different population types:
| Population | Mean and Variance Calculation | Total Exposures by Week Estimate |
| ---------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Everyone | Mean and variance across all users, estimated for 1, 2, 3, and 4 week rollups | Total count of users seen in the past 1, 2, 3, and 4 weeks |
| Targeting Gate | Mean and variance for users that pass the targeting gate, computed for 1, 2, 3, 4 week rollups | Total users that passed the targeting gate after 1, 2, 3, 4 weeks |
| Past Experiment | Cumulative mean and variance for the control group at 1, 2, 3, and 4 weeks | Total experiment exposures after 1, 2, 3, and 4 weeks, adjusted according to the past experiment's allocation and the target allocation for the new experiment. |
| Qualifying Event | Mean and variance for users who logged a specified event, computed for 1, 2, 3, 4 week rollups | Total users who logged a specified event after 1, 2, 3, 4 weeks |
## Analysis types
When viewing results, you can modify inputs such as "# of Groups", "Control Group %", and Analysis Types to update the power analysis results.
{% figure %}
{% /figure %}
### Fixed allocation analysis
If you already know the available allocation, fixed allocation analysis shows how the length of the experiment affects the MDE. The example below shows how the MDE for a page load metric shrinks over time in an experiment with 100% allocation. After 1 week, the expected user count per group is 5,200 with an MDE of 21.6%. By week 4, the user count per group increases to approximately 48,000 and the MDE is reduced to 7%.
{% figure %}

{% /figure %}
### Fixed MDE analysis
If you already know the effect size you want to measure, fixed MDE analysis shows the allocation and duration needed to achieve that MDE. Enter your target MDE as a percentage of the current metric value. For example, if a website gets 1,000 page loads per day, an MDE of 10% means a change of 100 or more page loads per day can be detected.
The results show the minimum number of weeks needed to reach this MDE for different allocation percentages. In the example below, the experiment must run for at least 2 weeks with 65% allocation or 4 weeks with 50% allocation. The target MDE can't be achieved in 1 week, because doing so would require more than 100% allocation.
{% figure %}

{% /figure %}
## Advanced options
{% figure %}

{% /figure %}
Advanced settings for customizing the analysis:
- **Number of Experiment Groups**: The total number of groups in the experiment, including control.
- **Control Group %**: What percent of users are in the control group, e.g. 50% if half of all users are control.
- **Fixed Allocation or Fixed MDE Analysis**: Different types of analyses you want to run. Refer to [analysis types](/experiments/power-analysis#analysis-types) for more details.
- **One-sided or Two-sided test**: Toggle this setting to select the type of z-test to use for the analysis.
- **Significant Level (α)**
- **Power (1-β)**
- **Bonferroni Correction Per Variant**: Whether to include α correction for multiple tests in power analysis.
## Calculation details
The relative percentage MDE for a given metric _X_ is computed using the following equation:
{% figure %}

{% /figure %}
- _X-bar_ is the mean metric value across all users
- _var(X)_ is the population variance of the metric
- _Ntest_ and _Ncontrol_ are the estimated number of users in the test and control group. These are based on historical active user data along with experiment allocation and group size.
- _Z1-β_ is the standard Z-score for the selected power. Typically _1-β = 0.8_ and _Z1-β = 0.84_
- _Z1-α/2_ is the standard Z-score for the selected significance level in a 2-sided test. Typically _α = 0.05_ and _Z1-α/2 = 1.96_
{% callout type="note" %}
This calculation relies on statistics computed across the entire user base of the project. It doesn't account for experiments that target only a subset of users, which may have different summary statistics for their key metrics.
For example, the metric mean and variance can differ in an experiment that targets only Android users or one that exposes users at the lower part of an acquisition funnel.
{% /callout %}
================================================================================
# Implement an Experiment
URL: https://docs.statsig.com/experiments/implementation/implement
Updated: 2026-07-11
================================================================================
To deploy an experiment, you need to:
1. Pull the experiment configurations in your application
2. Log the events you want in your experiment results
3. Test your experiment in development or a lower environment
4. Click "Start"!
Every experiment needs to expose users into more than one bucket (#1) and log metrics on their behavior after exposure (#2, called "log events"). Statsig automates the setup steps, such as generating the bucket-assignment code and running analysis on exposures and log events. The experimenter's job is to design the experiments and use the Statsig SDKs to accomplish steps 1 and 2.
## Pulling experiment configurations from Statsig
The code snippets below illustrate experimenting on a product demo flow, where you might experiment to improve conversion through the funnel to demo completion. For full implementation details, go to the [SDK documentation](/sdks/getting-started) for the language you are using, or go through the example guide for [your first A/B test](/guides/abn-tests).
```js
const user = { userID: loggedInUserID };
const demoConfiguration = statsig.getExperiment(user, "demo_experience");
// use parameters to control the experience
if (demoConfiguration.get("show_banner", false) {
showBanner();
}
const title = demoConfiguration.get("title", "Start Demo");
banner.setTitle(title);
```
You can also view a code snippet for your particular experiment by selecting the code snippet button on the experiment page and choosing the right SDK.
{% figure %}

{% /figure %}
## Logging events for your scorecard
To get experiment results for the events and metrics you care about, instrument the experience with the proper event logging (or set up an event integration or data warehouse import to send events to the Statsig experimentation stats engine). Using the Statsig SDKs, your code might look like this:
```
statsig.logEvent(user, "demo_started");
...
statsig.logEvent(user, "demo_completed");
```
A few events can help you measure how users move through a funnel in your product and enable you to experiment on those flows to increase conversion.
## Testing in a lower environment
After an experiment is launched, you can't edit the groups without restarting the experiment, because users are already being allocated to each group. Statsig recommends testing each experiment in lower environments before starting. Select the "Test" button in the experiment setup page, then select "Enable for Environments". These environments should match your [SDK environment setup](/guides/using-environments/#configuring-environments). Testing in a lower environment and using [overrides](/experiments/setup/overrides) lets you manually set your experiment group to test each variant.
{% figure %}

{% /figure %}
After the experiment is enabled for a lower environment, the experiment status shifts from “Not Started” to “Testing”.
{% figure %}

{% /figure %}
In the results section, you can track cumulative exposures and metric results collected from lower environments. Statsig displays results in aggregate across all lower environments and doesn't distinguish between individual environments. Statsig retains metric data and exposure data even when the experiment is repeatedly disabled and re-enabled for the lower environment.
You can resalt the experiment in lower environments. This is useful when you need to re-test the experiment end-to-end.
{% figure %}

{% /figure %}
## Starting your experiments
After your experiment has metrics, parameters, and a hypothesis, and you have tested it in a lower environment, select the "Start" button. Your experiment is immediately live in Production.
================================================================================
# Experiment Overrides
URL: https://docs.statsig.com/experiments/setup/overrides
Updated: 2026-07-11
================================================================================
## Override group allocation for an experiment
During development, explicitly stating which experiment group a user falls into can be useful. For example, if a developer is testing that each experiment condition works as expected, they might want to force themselves into each condition.
You can add overrides based on Feature Gates and Segments to create rules that force all users who pass a given Feature Gate or Segment into a given group. When fetching parameters for an experiment, if the user matches any override, Statsig returns the overridden result immediately.
{% callout type="info" %}
**Note**:
* For experiments that are in a layer, set the overrides at the layer level. Statsig can only override a user into one experiment within that layer (since layers always make experiments within them mutually exclusive).
* Add overrides before the experiment starts, or at least before a user's first exposure. Statsig excludes users whose first exposures are controlled by overrides from Pulse results, because these users aren't randomized. Adding a large number of users to the override can affect the reliability of your experimental results.
* **Warning**: if you add overrides after a user has already been exposed in an experiment, Statsig doesn't exclude that user from Pulse results. Statsig continues to attribute that user's events and metrics to the group they were first assigned to. However, Statsig applies and honors the override after you define it. This can dilute or pollute your experiment results, because Statsig attributes the user's actions to one group while they were actually exposed to a different group. The overall impact depends on how many users fall into this category.
* Statsig evaluates ID overrides first, then conditional overrides (from top to bottom).
* ID type used for ID Overrides may or may not match the experiment’s ID type.
{% /callout %}
{% figure %}

{% /figure %}
### Adding an override
* Log into the Statsig console at https://console.statsig.com
* On the left-hand navigation panel, select **Experiments**
* Select the experiment where you want to add an override, and navigate to the *Setup* tab
* Click **Manage Override**, configure the override, and select **Save**
{% figure %}

{% /figure %}
### Deleting an override
If you add an override but later decide it isn't needed, you can remove it so Statsig evaluates the rules as normal.
* Log into the Statsig console at https://console.statsig.com
* On the left-hand navigation panel, select **Experiments**
* Select the experiment or layer from which you want to delete the override
* Click **Manage Overrides**
* Click on the trash can icon next to the override you want to delete, and select **Save**
### Testing an override
After adding your override, test it in the "Check Group for a User" window by navigating to the **Diagnostics** tab and adding the properties required for a user to pass the gate or segment set as an override.
================================================================================
# Getting the Group
URL: https://docs.statsig.com/experiments/implementation/getting-group
Updated: 2026-07-11
================================================================================
A common misconception when working with experiments on Statsig is trying to check the experiment group in code. Checking the experiment group in code is an anti-pattern. It isn't necessary with Statsig, and it limits your ability to quickly test different variants.
Experiment groups are useful for understanding what a set of parameters represents in the Statsig console. Comparing "Sorted Long List" vs "Default Search Results" is easier to discuss than understanding what the `sorted = true, length = 10` parameters represent.
In code, checking parameters directly is more powerful and simpler to reason about. Rather than hard-coding a particular value, your variable is dynamically evaluated by Statsig. Parameters are the building blocks of your experiments in code, rather than group names.
### Example: group names vs parameters
Hard-coding experiment group names is fragile and limiting. Here is why.
In code, a function using experiment groups might look like this:
```ts
async function getSearchItems(user: StatsigUser, searchTerm: String): String[] {
const results = index.get(searchTerm);
const experiment = statsig.getExperimentSync(user, 'search_results');
// NOTE - these APIs don't actually exist - this is for the sake of an example
if (experiment.groupName === 'Sorted Long List') {
return results.sort().slice(10);
} else if (experiment.groupName === 'Sorted Short List') {
return results.sort().slice(3);
} else {
return results;
}
}
```
There are two problems with this code:
1. It is fragile. If the group name in code doesn't match the name in the Statsig console, the correct experience isn't returned.
2. It is static. Adding another experiment group, such as an "Unsorted long list", requires a code change.
So instead, this is what the code would look like using experiment parameters directly:
```ts
async function getSearchItems(user: StatsigUser, searchTerm: String): String[] {
let results = index.get(searchTerm);
const experiment = statsig.getExperimentSync(user, 'search_results');
results = experiment.get("sorted", false) ? results.sort() : results;
const numItems = experiment.get("length", 0);
return numItems > 0 ? results.slice(numItems) : results;
}
```
Your code is now completely decoupled from the names of experiment groups in the Statsig console. You are left with a set of dynamic parameters. You can create any experiment groups from these building blocks and the same code works. To test an unsorted list of 5 items against a sorted list of 20 items, configure it in the Statsig console and name the groups however you want.
If you were using group names instead, you would need to add conditions like:
```ts
} else if (experiment.groupName === 'Unsorted Short List') {
return results.slice(5);
} else if (experiment.groupName === 'XL Sorted List') {
return results.sort().slice(20);
}
```
Using parameters directly is much simpler and more flexible. It makes your code dynamic and offloads experimentation setup to the Statsig console. The group names you configure to describe each set of parameters make it easy to compare one group against another when analyzing experiment results.
## Rules
The diagnostics stream is for debugging your integration and understanding which groups a user is being assigned to. The following table defines the Rules you see and what they mean.
| Rule | Meaning |
|---|---|
| Not started | The experiment hasn't been started, so the allocation groups aren't determined yet |
| Holdout | The user is in a holdout that this experiment references, so they aren't in the experiment |
| Layer Assignment | The user isn't allocated to this experiment because they are bucketed to a different part of the Layer |
| Targeting Gate/Inline Targeting | The user doesn't meet the requirements for the targeting gate or inline targeting |
| Not Allocated | The user isn't allocated to this experiment because they don't meet the rollout % |
| \{group name}\{override name} | The user was forcefully bucketed into a given group by an experiment override |
| \{group name} | The user was bucketed into this experiment group |
| Abandoned | "Make Decision" selected the control group. This experiment has been abandoned. |
| \{group name} (Launched) | "Make Decision" selected this group as the launch group, so the user is seeing the launched experience. |
================================================================================
# Monitor an Experiment
URL: https://docs.statsig.com/experiments/monitor
Updated: 2026-07-11
================================================================================
After an experiment launches, you can monitor its health and exposure mix directly from the Statsig console.
## Experiment health checks
1. Open **Experiments** from the navigation.
2. Select the experiment you want to inspect.
3. Review the **Experiment Health Checks** banner at the top of the scorecard.
{% figure %}

{% /figure %}
Hover a status icon to read the summary, then click for full context. Common health checks include:
* **Checks started** - Verifies the SDK is reporting config checks shortly after launch.
* **Checks have valid unit type** - Confirms checks include the configured unit identifier (userID by default).
* **Event metrics have data** - Ensures events carry the same unit ID as exposures so Pulse can compute metrics. This often surfaces when downstream tooling (e.g., Segment) omits stableID or custom IDs.
* **Pulse metrics available** - Indicates Pulse results have landed (typically the day after launch).
* **Exposures are balanced** - Runs a chi-squared test for sample ratio mismatch (SRM). Occasional warnings happen due to randomness, but persistent red alerts point to assignment or logging issues.
* p-value between 0.001 and 0.01 -> Warning (yellow).
* p-value \< 0.01 with \<0.1% absolute deviation -> Warning (yellow) with low expected impact.
* p-value \< 0.001 and >=0.1% deviation -> Alert (red) requiring investigation.
* **Crossover units detected** - Flags users exposed to multiple variants. Statsig Cloud keeps these users in both groups (since the SDK rarely produces crossovers) but highlights them so you can address root causes. Reach out if you see rates above 1%.
* **Default value type mismatch** - Warns if an experiment's fallback default value type disagrees with the parameter definition.
* **Group assignment healthy** - Surfaces unexpected assignment reasons (e.g., `Uninitialized`, `InvalidBootstrap`). Click **View Assignment Reasons** to see the hourly breakdown.
{% figure %}

{% /figure %}
## Crossover troubleshooting
Crossover warnings usually mean:
1. The request bootstrapped with a different stable ID (`BootstrapStableIDMismatch`).
2. Both client and server SDKs are checking the same gate/experiment without synchronized updates.
If you can't pinpoint the cause, contact Statsig in Slack for help.
## Exposure streams
Scroll below the health checks to view exposure streams. These tables show every recent check, including the rule that matched and any secondary exposures (holdouts, targeting gates, etc.). They are useful for validating targeting and confirming ramp progress.
## Cumulative exposures
To track growth per variant:
1. Open the **Results** tab.
2. Locate the **Cumulative Exposures** chart.
{% figure %}

{% /figure %}
The chart shows how many users have entered each group over time, making it easy to spot ramp issues early.
Monitoring these diagnostics helps you resolve issues quickly and keep experiments on track.
================================================================================
# Experiment Quality Score
URL: https://docs.statsig.com/experiments/setup/quality-score
Updated: 2026-07-11
================================================================================
## How experiment quality score works
The Experiment Quality Score is a metric that provides a quick measure of the quality and trustworthiness of an experiment configured in Statsig. The score helps experimenters and their peers quickly identify potential issues in experiment setup, execution, and data collection, enabling more confident decision-making. Measuring this score across all experiments helps teams discover systematic issues and identify opportunities to improve their experimentation program over time.
## Configure experiment quality score
To enable the Experimentation quality score, go to *Settings > Experimentation > Experiment Quality Score* in the project settings.
Statsig evaluates experiments against a list of pre-defined assessment criteria. You can customize the weight of each criterion based on your organization's needs, though Statsig provides default values.
{% figure %}

{% /figure %}
## Advanced configuration
For organizations with more complex requirements, you might need additional checks, different requirements per product team, or thresholds that differ from the defaults. For example, hypotheses might need to be at least 200 characters and contain a link to an external planning doc.
To manage these requirements, use the Statsig console API. Run a POST or PATCH on the `console/v1/experiments` endpoint to update individual scores on any experiment. Targeting the existing set of scores lets you override weights (usually to 0), so the list contains only the custom set you need.
For example, running patch on an experiment with this payload:
```
{
"manualQualityScores": [
{
"criteriaName": "HYPOTHESIS_LENGTH",
"criteriaDescription": "Check passed",
"status": "PASSED",
"score": 0,
"weight": 0
},
{
"criteriaName": "MyCompany\'s Hypothesis Check",
"criteriaDescription": "Has Internal URL and > 200 Chars",
"status": "PASSED",
"score": 100,
"weight": 100
},
{
"criteriaName": "Naming",
"criteriaDescription": "Experiment prefixed with team name",
"status": "FAILED",
"score": 0,
"weight": 100
}
]
}
```
This payload would:
* Drop the original HYPOTHESIS\_LENGTH check
* Keep the other original checks, with their weights
* Add a new check, `MyCompany's Hypothesis Check`, for custom logic on the hypothesis
* Add a new check, `Naming`, for custom logic on the name
Statsig normalizes the other weights. If the original HYPOTHESIS\_LENGTH had a weight of 10, the total weight would be 290 and scores normalize accordingly. If all non-custom checks were passing, the score would be 190/290 or ~66%.
The general flow for using this approach:
* Use the Console API's `experiments/get` to pull all experiments
* For each experiment:
* Run custom logic
* Patch results
## How scores are calculated
Statsig skips checks in an unready state during evaluation and renormalizes the other weights to 100%. For example, if the experiment hasn't started, the `Balanced Exposures` component is in an unready state and ignored.
Statsig omits checks with a weight of 0 from the card entirely.
## Viewing quality scores
When enabled, quality scores appear in the details tab of an experiment. Statsig evaluates each applicable check and contributes it to the displayed score.
Statsig color-codes the score based on the threshold it reaches.
* \>= 85% corresponds to passing/green
* \>= 50% corresponds to warning/yellow
* \< 50% corresponds to error/red.
{% figure %}

{% /figure %}
Quality scores are also available through the console API, which you can use for bulk data retrieval and analysis.
================================================================================
# Switchback Tests
URL: https://docs.statsig.com/experiments/types/switchback-tests
Updated: 2026-07-11
================================================================================
## What is switchback testing?
Switchback tests are an alternative experiment form in which an entire population switches back and forth between test and control treatments on a set cadence, rather than being split and evenly divided between test and control for the duration of the experiment.
Switchback tests are particularly common in marketplaces. Running a traditional A/B test on one side of the marketplace can have unintended consequences on the rest of the marketplace due to network effects, which affect experiment results.
Another common use case for switchbacks is when applying different variants to different users isn't feasible for fairness, legal, or logistical reasons.
Switchback tests are often carried out across multiple "buckets", typically regions or other defined groups that are flipped between test and control treatments over the course of the experiment.
### Example
Consider a rideshare platform that wants to test pricing. The initial approach splits riders into two groups: one with a higher price and one with a lower price.
Riders with the lower price request rides at a significantly higher rate, consuming all available driver supply in a given area. This leaves riders with higher prices facing not only a higher ride estimate but also longer ETAs, making them even less likely to request a ride.
The experiment results become unclear: the decreased ride request rate in the higher-price group could be caused by the higher prices or by the longer ETAs. The experimental design has introduced bias into the results.
A switchback test resolves this. Instead of splitting users, 100% of riders and drivers in a given metro switch in and out of the new pricing plan hourly. The test then measures the impact on overall ride request rates during hours when prices were higher versus lower.
## Switchback testing on Statsig
### Methodology
The switchback testing methodology for computing results consists of 3 steps:
1. Attribute events to the corresponding switchback bucket, where each bucket is defined by the time window and grouping attribute.
2. Calculate the variant-level and bucket-level metrics based on the attributed events.
3. Calculate the difference in means between test and control. Use bootstrapping to obtain the confidence intervals.
### Event attribution
Attribution of events to a particular bucket is based on the timestamp and unit_id of the exposure, the length of the attribution window, and the timestamp of subsequent events for that unit_id.
For example: User 123 is exposed to bucket A at 9:15 AM. The test has an attribution window of 90 minutes. All events triggered by user 123 between 9:15 AM and 10:45 AM are included in the metric calculations for bucket A.
### Bucket-level metrics
Once Statsig has all the events corresponding to a bucket, it calculates the scorecard metrics derived from these events.
{% figure %}

{% /figure %}
For sum and count metrics, Statsig uses the mean value per unit exposed to that bucket.
{% figure %}

{% /figure %}
### Variant-level metrics
Statsig calculates overall metric means for test and control by aggregating values across all buckets in that variant. If there are **M** buckets in the test group, the mean value of a ratio metric is given by:
{% figure %}

{% /figure %}
The mean of a sum or count metric would be:
{% figure %}

{% /figure %}
### Deltas and confidence intervals
The treatment effect is calculated as:
{% figure %}

{% /figure %}
The bootstrapped confidence intervals are obtained as follows:
1. Collect a bootstrap sample with replacement from the set of test buckets and separately from the set of control buckets.
2. Calculate the difference in means between test and control samples.
3. Repeat steps one and two 10,000 times to produce a distribution of metric deltas.
4. The 95% confidence interval is the range from the 2.5% quantile to the 97.5% quantile of that distribution. In general, the confidence interval with significance level $\alpha$ is given by:
{% figure %}

{% /figure %}
## Setup
To set up a switchback test on Statsig, when you create an experiment tap **Advanced Settings** → **Experiment Type** and select "Switchback Test".
{% figure %}
{% /figure %}
Switchback test configuration adds two new aspects to the standard experiment setup:
1. **Targeting**: The defined population(s) you run your experiment on.
2. **Schedule**: The switching frequency and starting treatments for different pre-defined populations.
There are two ways to define targeting:
- **Targeting Gate**: Specify a targeting gate to define your target experiment population, the same as any other experiment on Statsig.
- **Bucketing Method**: Bucket users based on either pre-defined buckets or randomized across an ID type.
{% figure %}

{% /figure %}
**Buckets** let you specify pre-defined buckets, such as _Country_, _Locale_, or a _Custom Field_ you log. Use this option when you have a few pre-defined populations you want to switch in and out of Test/Control over the course of the experiment.
{% figure %}

{% /figure %}
**ID Type** lets you specify an ID type to randomize across. For example, choosing a custom ID such as CityID automatically randomizes different CityIDs across Treatment/Control over the different switchback windows. Use this option when you have a very large or dynamic number of experiment units to randomize across.
{% callout type="info" %}
Randomized bucketing is an advanced feature. Reach out to the support team, your sales contact, or the [Slack community](https://statsig.com/slack) to have this enabled.
{% /callout %}
{% figure %}

{% /figure %}
Depending on which bucketing method is selected, the **Schedule** section of experiment setup lets you configure:
- Start time
- Duration (in days)
- Assignment window size (in minutes)
- Burn-in/ burn-out periods (in minutes)
- _(Pre-defined bucketing only)_ Starting phase (treatment group) for each bucket
{% figure %}
{% /figure %}
Burn-in/burn-out periods let you define time intervals at the start and end of your switchback windows to discard exposures from analysis. Use these when there are risks of bleed-over effects from the previous treatment while a population is switching between test and control.
## Reading results
Diagnostics and Pulse metric lift results for switchback tests resemble Statsig's traditional A/B tests, with a few differences:
- **No hourly Pulse:** Because a switchback experiment starts with all-Test or all-Control exposures, hourly Pulse is disabled until there is a meaningful amount of data. Use the **Diagnostics** tab in the meantime to verify checks are arriving and bucketing as expected.
- **No time-series:** The Daily and Days Since First Exposure time-series aren't available for switchback tests. The bootstrapping methodology requires pooling all available days together to achieve sufficient statistical power.
- **No dimension breakdown:** Breaking down a metric by user property or event property isn't available for switchback tests.
- **Advanced statistical techniques:** CUPED and Sequential Testing aren't yet available for switchback tests.
{% figure %}

{% /figure %}
================================================================================
# One-Sided Test
URL: https://docs.statsig.com/experiments/statistical-methods/methodologies/one-sided-test
Updated: 2026-07-11
================================================================================
## One-sided tests (aka one-tailed test, non-inferiority test)
A one-sided test lets you test for a metric moving in only one direction, which you specify in advance. This trade-off gives you additional sensitivity (or power) compared to the standard Pulse results, which show two-sided results by default.
Use cases for one-sided testing include detecting regressions in guardrail metrics and testing for a change where only one direction has meaningful business impact. For example, you may not care whether a new feature reduces crash rates, but you do care whether the new feature increases crash rates. In that case, you are willing to forgo detecting the reduction in favor of better detecting the increase.
{% callout type="info" %}
One-sided tests don't detect metric movement in the unspecified direction. They give higher sensitivity in the direction of interest by allocating all alpha to that direction. This results in one-sided confidence intervals (CIs) that are narrower in the direction of interest than their two-sided counterparts.
{% /callout %}
## How to enable one-sided tests
When setting up an experiment and identifying metrics to measure, the default setting is to run a two-sided test. To change this, click the metric name on the experiment setup screen. A popup opens where you can modify the test type and indicate the direction you want to measure.
{% callout type="note" %}
V1 doesn't support Bayesian testing yet.
{% /callout %}
{% figure %}

{% /figure %}
## How to read one-sided test results
Metrics using one-sided tests appear in Pulse similarly to two-sided tests. The difference is that Pulse shows a one-sided CI rather than a two-sided CI.
One-sided CIs extend to infinity or negative infinity, because Statsig only detects changes in the specified direction. As with all CIs, the one-sided CI indicates that the true mean value of the metric likely falls within that range. Because the CI is wide on the unbounded side, you can also interpret the result as high confidence that the mean doesn't fall outside the CI.
{% figure %}

{% /figure %}
## FAQ
#### Why can't I run two one-sided tests?
Running two one-sided tests results in a less powerful test. One-sided tests work by allocating all Type I error (alpha) to one direction. Adding a second one-sided test in the other direction reintroduces the chance of a Type I error in that direction. This produces confidence intervals that appear tighter than warranted, leading to higher decision error rates than the specified confidence level (default 95%).
#### Why use a one-sided test rather than a two-sided test?
The choice depends on your use case, the metric of interest, and the business impact of any decision. Select a one-sided test when detecting a change in only one direction is valuable and you can accept not detecting changes in the other direction.
================================================================================
# Running an A/A Test
URL: https://docs.statsig.com/experiments/types/aa-test
Updated: 2026-07-11
================================================================================
This guide walks through creating and implementing an A/A test on your product in Statsig. A/A tests are commonly used to validate a new experimentation engine you may be integrating with.
For new users, running an A/A test provides a low-stakes first test environment. Use it to confirm that your metrics are set up correctly and that exposures are flowing through as expected before starting your first A/B test.
By the end of this tutorial, you'll have:
* Created a new **Feature Gate** in the Statsig console, set up as an "A/A test"
## Prerequisites
1. You already have a [Statsig account](https://console.statsig.com/sign_up)
2. You already [integrated the Statsig Client SDK](/sdks/quickstart) into an existing application
## Step 1: Create a feature gate in the console
The easiest way to run an A/A test in Statsig is by using a [Feature Gate](/feature-flags/overview). You can also use an [Experiment](/guides/abn-tests) to run an A/A, but this tutorial uses a Feature Gate.
Log into the Statsig console at https://console.statsig.com/ and navigate to **Feature Gates** in the left-hand navigation panel.
Click on the **Create** button and enter the name and (optional) description for your feature gate. This example calls the feature gate “aatest\_example”. Click **Create**.
{% figure %}

{% /figure %}
In the Setup tab, define the rules for this feature gate. Tap **+ Add New Rule**. You can run an A/A test on a specific user group or platform, but the recommended setup is to divide all traffic 50/50 and deliver the same experience to each group.
{% figure %}

{% /figure %}
Under **Criteria**, select **Everyone** (you may need to scroll up), name your rule, and set the **Pass Percentage** to 50%. Click **Add Rule**, then tap **Save Changes** in the upper right-hand corner.
{% figure %}

{% /figure %}
Your feature gate setup should now look as follows:
{% figure %}

{% /figure %}
Verify that the gate is working as expected by typing in some sample user IDs into the console. Roughly 50% of the IDs should pass and 50% should fail.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
## Step 2: Check the feature gate in your code
Copy the code snippet from the upper right corner of your feature gate page (under the **\< >** symbol) and add it to your application at the point where you want to call the A/A check.
```jsx
statsig.checkGate("aatest_example")
```
When a user renders this page in their client application, a live log stream appears in the Statsig console under the **Diagnostics** tab of your feature gate.
{% figure %}

{% /figure %}
## Step 3: Review A/A test results
Within 24 hours of starting your experiment, cumulative exposures appear in the **Pulse Results** tab of your feature gate.
{% figure %}

{% /figure %}
This shows your logged exposures and their distribution. If something looks off, check the **Diagnostics** tab for day-by-day exposure breakdowns at both the Checks and User level.
In the **Metric Lifts** panel, you can see the full picture of how all your tagged metrics are performing.
{% figure %}

{% /figure %}
What should you expect to see?
* **Exposures**- make sure you’re seeing exposures flowing through as expected from your product. If you’re not seeing exposures, use the **Diagnostics** tab and the **Exposure Stream** to debug
* **Pulse results**- roughly 5% of your metrics in Pulse should be showing a statistically significant change due to the 95% confidence interval of Statsig’s stats engine
Run your A/A long enough to reach most of your weekly active users, or at least a week.
## Simulated A/A tests
Statsig runs simulated A/A tests every day in the background for every company on the platform. An A/A test is structured like an A/B test, but both groups receive the same experience. A/A tests help build trust in your experimentation platform and metrics.
A/A tests can be Online or Offline. An [Online A/A test](/experiments/types/aa-test) runs on real users. An engineer instruments your app with the Statsig SDK to check for experiment assignment. Statsig logs assignment, but delivers no difference in experience to the user.
Since there is no effect, you expect to only see statistical noise. When using 95% confidence intervals, only ~1 in 20 metrics shows a stat-sig difference between control and test.
### Offline A/A tests
A single request runs on one unit type. An offline A/A test works by:
1. Querying a representative sample of your data
2. Randomly assigning subjects to Test or Control
3. Computing relevant metrics for Test vs Control and running them through the stats engine
4. Measuring the percentage of false positives. With a p-value cutoff of 0.05 (typical), you can expect a ~5% false positive rate.
Download the running history of your simulated A/A test performance through the “Tools” menu in the Statsig Console. Statsig runs 100 tests per request.
### Simulated A/A test file columns
| Column Name | Description |
| -------------------------------- | ------------------------------------------------------------------- |
| metric\_name | Name of the Metric |
| metric\_type | Type of Metric |
| unit\_type | The unit used to randomize (e.g. userID) |
| n\_tests | The number of tests run |
| pct\_ss\_95\_pct\_confidence | The percentage of tests that have a stat-sig result for this metric |
| avg\_units\_per\_test | The number of units (often users) sampled into the A/A test |
| avg\_participating\_units\_per\_test | The number of units in the test with a value for this metric |
{% figure %}

{% /figure %}
================================================================================
# SEO Experimentation with Statsig
URL: https://docs.statsig.com/experiments/types/seo-testing
Updated: 2026-07-11
================================================================================
In late 2017, Airbnb's growth team faced a deceptively simple question:
> Would their new "Magic Carpet" landing page design drive more organic traffic than their existing search results page?
With over 100,000 unique URLs spanning different cities, any template change would cascade across their entire search footprint. Traditional A/B testing couldn’t solve this puzzle because Google’s crawlers needed consistent page versions, making user-level randomization impossible.
Every marketplace with thousands of templated pages faces the same dilemma: measuring how changes to your template actually impact how Google ranks your pages. This is true whether you’re dealing with physical goods at **Amazon** or **eBay**, or more virtual things at **ZipRecruiter** or **Eventbrite**.
Companies can implement the same framework Airbnb developed in hours, not months, and view results in the same dashboards they already use for product experiments.
***
## 1. Select a deterministic page bucket
* Crawlers must see a consistent version of each URL during the test window, so you **can’t randomize by user**.
* Instead, hash the **canonical URL** into buckets.
* In Statsig, you formalize this by adding `page_url` as a **Custom Unit ID**.
Steps:
1. From **Project Settings**, navigate to **Custom Unit IDs**.
2. Provide a name and description (it then immediately becomes available to experiments, gates, and dynamic configs).
Statsig can now deterministically hash your pages into **Control vs Test** in experiments and keep this assignment stable across sessions.
{% callout type="note" %}
Strip out `http` vs `https` and query params, leaving only the stable base URL, so that is what is hashed deterministically.
{% /callout %}
***
## 2. Define metrics before shipping
Make sure the metrics you want to measure are in your data warehouse, keyed on `page_url`. Register these with Statsig’s **metric catalog**. Because the same pipeline powers feature experiments, your existing CUPED or stratified-sampling settings apply automatically.
### Example metrics
| Layer | Metric Source | Why it Matters |
|------------------|--------------------------------------------------|-------------------------------------------------|
| Indexing lag | Impressions, average position (Search Console) | Early signal during re-crawling |
| Primary KPI | Organic sessions keyed by `page_url` (Statsig Events) | Measures traffic that actually lands |
| Quality guardrail| Conversion, bounce, read-depth, revenue | Ensures traffic is useful |
***
## 3. Implement the change behind an experiment
* Create an experiment called `seo_title_test` in Statsig Docs.
* Target on the **Custom Unit ID `page_url`** with a 50/50 split across Control and Test.
* Expose the variant in the template renderer or CDN edge function.
***
## 4. Ship, monitor, decide
* Use **Power Analysis** to determine how long your experiment should run based on traffic volume.
* Expect first signals in 2–7 days; wait for re-indexing to plateau before results stabilize.
* Merge the winner into your template and archive the test. Experiment summaries remain searchable after archival.
### SEO-specific guardrails
| Guardrail | What to Watch | Why it Protects You |
|----------------------|-------------------------------|---------------------|
| Indexation Δ | `indexed_pages` vs baseline | A template tweak that blocks crawl (robots, canonicals, noindex) will show a sharp drop long before traffic falls |
| Cannibalization ratio| Avg. URLs served per query | Multiple pages newly ranking for the same query dilute CTR and can tank combined traffic |
| HTTP response mix | % 410 vs 301 vs 200 | A bulk 410 (gone) or mis-configured 301 can wipe out long-tail pages |
| Core Web Vitals drift| LCP & CLS p75 | Page-speed regressions may hurt rankings silently |
| Crawl budget | Avg. TTFB + bytes/page | Slow/bloated pages decrease crawl rate |
***
## 5. Concrete page-level changes worth A/B testing
| Theme | Why It Might Move Organic Traffic | Typical Implementation Knob |
|-----------------------|-----------------------------------|-----------------------------|
| Title & meta variants | Query-matching, CTR uplift | Add/remove brand suffix, noun → verb phrasing, insert dynamic price |
| Structured data | Rich-result eligibility | Inject FAQ, HowTo, Breadcrumb, or Product schema blocks |
| Internal-link blocks | Crawl priority & PageRank flow | Swap “related articles” widget ordering; test link count caps |
| Content snippets | Relevance & long-tail keywords | Auto-generate 50-word intro vs. none; expand FAQ length |
| Canonical/hreflang tags | Duplicate-content handling | Toggle self-canonical vs. cluster canonical; add `hreflang="x-default"` |
| Media handling | CLS/LCP scores influence rankings | Defer off-screen images; inline critical hero image; switch to AVIF |
| Pagination model | Crawl depth & index coverage | Classic `?page=` URLs vs. `rel="next/prev"` vs. load-more buttons |
| Performance budgets | Core Web Vitals ranking factor | 200 ms JS chunk split vs. baseline; CSS purge + inline critical-CSS |
| Ad layout | CLS penalties, user engagement | Reserve fixed ad slots vs. dynamic; lazy-load below first viewport |
| Schema position | Parser friendliness | Move JSON-LD block to `
` vs. end of `` |
***
## 6. Is SEO experimentation right for you?
| Great Fit | Maybe Not Yet |
|---------------------------------------------------------------------------|--------------------------------------------------------------|
| Large page surface (10k+ URLs): marketplaces, docs, publishers | Marketing sites with \<1k pages or sporadic organic traffic |
| Teams already shipping **weekly** and want proof before rollouts | Heavy paid-ads model where SEO is \<5% of acquisition |
| Companies with engineering bandwidth to template page changes | Sites on locked-down CMSs that forbid code/tag edits |
***
## 7. Key takeaways
* **Segment by page, not user.** Use a Statsig Custom ID for deterministic hashing into Control/Test.
* **Measure beyond clicks.** Pair Search Console data with product analytics for full-funnel insight.
* **Catch problems early.** Statsig’s sequential engine and guardrails identify underperforming variants before they cause harm.
* **One platform for every test.** Product, pricing, UX, and SEO experiments in a single, trusted workflow.
***
{% callout type="note" %}
Statsig also supports other experiment types such as **switchback testing** and **geo-testing**.
Geo-testing is particularly useful for measuring the **incrementality of ad spend**, which is hard to measure with traditional experiments due to privacy requirements.
{% /callout %}
================================================================================
# Contextual Bandit (Autotune AI)
URL: https://docs.statsig.com/autotune/contextual/introduction
Updated: 2026-07-11
================================================================================
Contextual Multi-Armed Bandits are a subset of Multi-Armed Bandits that use context about a user to personalize their experience. Autotune AI predicts outcomes for each variant and selects the best outcome while accounting for uncertainty. Autotune AI prefers variants with high uncertainty over variants with slightly better predictions but low uncertainty, which drives exploration.
## Use cases
Contextual bandits bridge the gap between un-personalized solutions and fully fledged ranking solutions. The main limitation is that contextual bandits:
* Have a fixed output set of variants they can show
* Have limited ability to account for complex context on the "object" being seen/predict for novel content (e.g. video ranking)
Their simplicity also provides advantages. Statsig's Autotune AI evaluates in near-real-time on both the server and client, taking a few milliseconds or less to return the ideal experience for a given user context. Contextual bandits are also simple to set up and test: you can set up a test in less than an hour, get model results the next hour, and start seeing experiment results the hour after that in the Statsig console.
For more discussion on use cases and motivations, refer to the [Statsig blog](https://www.statsig.com/blog/statsig-autotune-contextual-bandits-personalization).
## Methodology
Statsig's autotune AI uses a LinUCB based approach. This paper is a good introduction to the topic: [Li, Chu, Langford, Schapire](https://arxiv.org/pdf/1003.0146). For coverage of regret analysis, these lecture notes from [Jain from the University of Washington](https://courses.cs.washington.edu/courses/cse599i/18wi/resources/lecture10/lecture10.pdf) are a useful resource.
Autotune AI works with categorical and numerical features. Key-value pairs attached to the custom object on the Statsig user are converted into categorical or numerical features based on their data type. Categorical features are one-hot-encoded. You don't need to build complex training pipelines, though many customers pass pre-evaluated user attributes or predictions as context objects.
{% callout type="note" %}
Statsig's console supports specifying features up front. This can help you identify which features to fetch for the bandit when lookups are expensive or live. For Warehouse Native customers, planned work will allow joining entity properties during analysis, enabling you to plug in your own feature store for Autotune AI analysis, similar to the approach used with [CURE](/statsig-warehouse-native/cure/introduction).
{% /callout %}
Statsig selects the best model (for example, Ridge or Logistic regression) based on your data types and performance, then generates a model from your data. The estimated standard error of the model produces a prediction confidence interval. During evaluation, Statsig uses user context to predict an outcome for each variant and applies the corresponding confidence interval to that prediction. The best variant is the one with the highest upper end of a 95% confidence interval. To adjust the interval size, modify the exploration parameter on the Autotune setup page.
For a detailed discussion, refer to the [Methodology](/autotune/contextual/methodology) page.
{% callout type="note" %}
You can also fetch a ranked list from Statsig and manually expose the variants you show to the user. This is useful when you have client-side filtering or want to show multiple options. Refer to [Advanced Usage](/autotune/using-bandits).
{% /callout %}
## Drawbacks
Because Statsig manages the models, it can't guarantee perfect model tuning, and more advanced models such as neural networks aren't available. If recommendations are a critical business problem, this feature can serve as a starting point but isn't an appropriate long-term solution.
The current approach balances simplicity, speed, and regret minimization. Specific use cases such as real-time updates may not be fully supported.
Because the models generally assume linearity, they may not capture complex user interactions. This approach works best for broad-level effects, though feature interaction terms can provide reasonable predictive power for conditional relationships between predictors and outcomes.
## Outcome types
Autotune AI supports multiple model types internally, covering both classification use cases (for example, whether a user clicks a button) and continuous outcomes (for example, how much time a user spends reading articles). You can optimize for both "outcomes" and "metrics." To minimize a metric such as latency, disable the "higher is better" setting for that metric.
For classification cases, Autotune AI identifies whether any outcome occurs within its attribution window. For continuous cases, Autotune AI requires an event name and field name, and uses the numerical value associated with that field.
## Training
Training pipelines are run hourly.
For Warehouse Native customers, Statsig processes data in your warehouse and uses an anonymized feature set to train the models. Statsig exports exposures on-demand for each load up to the first million, and in daily batches after that. Log events sent to Statsig are exported hourly if you use Statsig to log outcomes, or you can connect metric sources from your warehouse for outcome tracking.
For cloud customers, Statsig processes and trains the data entirely on its servers.
## SDK support
Statsig supports contextual autotune in all Client SDKs, but only in the following server SDKs:
* [Node](/server-core/node-core)
* [Python](/server-core/python-core)
* [Java](/server-core/java-core)
* [Elixir](/server-core/elixir-core)
* [Rust](/server-core/rust-core)
* [Go](/server/go) v1.39.0+
* [Ruby](/server/ruby) v2.4.0+
================================================================================
# Get Started with Autotune AI
URL: https://docs.statsig.com/autotune/contextual/getting-started
Updated: 2026-07-11
================================================================================
Autotune AI is quick to set up.
{% callout type="warning" %}
Statsig supports contextual autotune in all Client SDKs, but only in the following server SDKs:
* [Node](/server-core/node-core)
* [Python](/server-core/python-core)
* [Java](/server-core/java-core)
* [Elixir](/server-core/elixir-core)
* [Rust](/server-core/rust-core)
* [Php](/server-core/php-core)
* [Ruby](/server/ruby)
{% /callout %}
## Set up your Contextual Autotune
Configure your contextual autotune in the Statsig console. You can also configure it programmatically through the Statsig console API.
## Create the contextual autotune
Log in to your Statsig console and navigate to Autotune under Experiments.
{% figure %}

{% /figure %}
Select **Create**. Name your contextual autotune and, optionally, specify the goal so other users can understand the motivation behind it.
{% figure %}

{% /figure %}
Set your autotune type to **Contextual**.
{% figure %}

{% /figure %}
## Configure optimization
If optimizing for discrete outcomes such as:
* User clicks
* Checkouts
* Actions
Choose **Event Occurring** as the outcome type. If optimizing for a continuous output such as:
* Revenue
* Latency
Choose **Event Value** and set the directionality. Also choose the field from your log or metric source (Warehouse Native) to use for the value.
For Warehouse Native customers, specify the metric source and any optional filters for your target event.
{% figure %}

{% /figure %}
Wrapping contextual autotunes in an experiment is highly recommended but not required. You can set this up before or after your contextual autotune. The experiment wraps autotune calls in code and measures the overall impact of using this contextual autotune in your project.
## Training settings
The remaining settings have defaults, but you can tune them as needed:
* **Exploration window:** How long to serve traffic randomly to bootstrap bandit exploration.
* **Attribution window:** How long after a user sees your variant to count outcome events. If set to 1 hour, a user has 1 hour to take action after seeing the experience.
* **Exploration rate:** Controls how much the bandit favors exploration over exploitation. 0 uses only the best prediction without confidence intervals. 1 uses the 99.9% CI instead of the 95% CI for exploration.
* **Long-term exploration allocation %:** The percentage of traffic that is always randomly assigned. Use higher values for contextual autotunes you plan to run for a long time, to help avoid model drift.
* **Feature list:** A list of features Statsig should use to train the model. This acts as a filter; if not set, Statsig reads every custom attribute. The main use case is fetching this through CAPI to understand which features a given contextual autotune requires for on-demand evaluation.
{% figure %}

{% /figure %}
Set up your variants. Variants are configurations you fetch in code. For example, the configuration below sends a "red" value to your codebase, which can be passed to the color setting on a button.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
## Use the Contextual Autotune in code (Python example)
The following code assumes you have your server secret key. Before running Python, install the SDK:
`pip install statsig-python-core`
First, import and initialize Statsig:
```
from statsig_python_core import Statsig, StatsigUser
key =
autotune_name =
statsig = Statsig(key)
statsig.initialize().wait()
```
Then, create a user object and fetch your config:
```
user = StatsigUser('user_id', custom={'key1': 'value1', 'key2': 'value2'})
cfg = statsig.get_experiment(user, autotune_name)
```
Now you have your config and can apply it:
```
color = cfg.get_string("color", "default color")
print(f"Going to use {color} for my color now")
```
At this point, you should be able to:
* Confirm that you fetched a value from one of your variants.
* Navigate to the diagnostics page of your autotune and see a log of the user ID along with the corresponding variant.
Your code is now serving personalized variants to your users.
## Important considerations
Statsig requires a few hundred units to train a model and doesn't start training until those units' attribution window has elapsed. To test the functionality, you can simulate events to confirm things work as expected. Use logic like:
```
fetch_autotune_value()
if(user country == 'us'):
log_click()
```
to conditionally send events and verify that the model picks up on the conditional behavior.
================================================================================
# Monitoring your Contextual MAB
URL: https://docs.statsig.com/autotune/contextual/monitoring
Updated: 2026-07-11
================================================================================
There are three primary ways to monitor autotune performance.
## Linked experiments
The most reliable way to evaluate whether a bandit is working is to measure whether it drives more of the targeted behavior compared to a baseline experience. You can set up and link an A/B test in Statsig to evaluate this, which also lets you monitor other user behaviors and guardrail metrics.
This is the gold standard of measurement and is highly encouraged.
Standard practice is to wrap the autotune in an experiment with a binary parameter, either as 50/50 or a 90/10 holdback. You can link the experiment to the autotune to get the results on the autotune page. In code, this might look like:
```
experiment_value = statsig.get_experiment('wrapping_experiment').get('flag')
default_param = '..."
if(experiment_value):
param = statsig.get_experiment('autotune').get('param_name')
else:
param = default_param
# use param in code
```
You would start this experiment at the same time that you launch your autotune.
## Success rate
Statsig tracks the cumulative and daily success rate of your variants over time. This can be tricky to interpret. For example, variant A may have lower CTR, but the users being served variant A might have had even lower CTR on other variants. Use this view for tracking and understanding, and to identify outlier variants with notably high or low performance.
## Traffic allocation
Traffic allocation shows where Statsig is sending users who see your Autotune. Use this view to identify whether a variant is dominating traffic or receiving no traffic.
## Model features
Statsig tracks and surfaces coefficients and feature importance; this can be very useful for understanding which features might be worth further study, or which populations may have unmet needs in your product.
* Importance is an estimate of the influence of a feature on the outcome: how much the feature contributes to the prediction.
* A positive coefficient means that feature leads to an outcome being more likely (or for continuous outcome spaces is associated with a higher outcome). A negative coefficient means the outcome is less likely, or is associated with a lower continuous outcome.
================================================================================
# Contextual Bandit Methodology
URL: https://docs.statsig.com/autotune/contextual/methodology
Updated: 2026-07-11
================================================================================
## How contextual bandits work
This page covers the high-level approach that Statsig takes to running contextual bandits across cloud and Warehouse Native deployments. Implementation details change frequently as Statsig experiments and optimizes its approaches, so this documentation is intentionally high-level.
## Core approach
The implementation follows the disjoint model methodology from [Li, Chu, Langford, Schapire](https://arxiv.org/pdf/1003.0146). One model is trained per variant, and estimated confidence intervals (CIs) are computed for each. When contextual autotune is triggered, the latest model version estimates the user's outcome and adds the upper end of the 95% CI to that estimate.
{% figure %}

{% /figure %}
Statsig models categorical outcomes as logistic regression with L2 penalty. Statsig models continuous outcomes as multivariate ridge regression.
## Training data and sampling
To keep data relevant, contextual autotune data is upsampled to prefer recent dates. Sampling uses these mechanisms:
* A flat number of samples is selected, preferring the most recent records.
* Per day, over the last two weeks, samples are chosen to prefer more recent records.
* Samples from the explore dataset are strictly preferred, but non-explore data may be used to satisfy sample requirements. Records are then prioritized by a unit-ID hash to maintain stability in the training set between runs and avoid major jitter.
* A sample set is chosen per variant to avoid bias from a dominant model being overrepresented in the training data.
If a model has very low volume, it has low representation in the training data. Lower representation causes higher CIs, which increases the upper bound and makes that variant more likely to be selected. This acts as a bounce-back mechanism for low-traffic variants.
## Model and feature updates
Models are updated hourly. If a model definition changes (features or target outcome), all data is reset, and the model retrains to match the new definition on the next hourly update.
The pre-training data pipeline is available in the history view on the results page, showing the SQL used and the caching tables where data is stored. You can use this data to validate or explore modeling approaches.
## Feature encoding
Features with numerical-only values are treated as continuous random variables. All others are string-encoded and one-hot-encoded into binary variables for regression, using the top 25 levels available in the data with more than 1% coverage.
Arrays of categories or tags aren't supported, or encode only the most common tag sets. Provide tags as individual key-value pairs in the user custom object instead.
## Model monitoring
Diagnostics for model characteristics over time aren't currently available. Model coefficients are visible in the results tab. You can view a comparison of performance between naive random traffic and targeted traffic to determine whether model performance relative to blind allocation improves or degrades over time.
================================================================================
# Autotune (Bandits)
URL: https://docs.statsig.com/autotune/overview
Updated: 2026-07-11
================================================================================
Autotune and Autotune AI are Statsig's Multi-Armed Bandit solutions. They automatically find the best variant among a group of candidates while dynamically allocating traffic to optimize for a single target metric.
Autotune, the Multi-Armed Bandit solution, allocates traffic toward high-performing variants and can eventually identify a winning variant.
## How Autotune works
Autotune is Statsig's [Bayesian Multi-Armed Bandit](./multi-armed-bandit). It tests and measures different variations and their effect on a target outcome. The multi-armed bandit continuously adjusts traffic toward the best-performing variations until it can confidently pick the best variation, which then receives 100% of traffic.
Bandits balance the explore/exploit problem: exploiting the current best-known solution versus exploring to gather more information about other solutions.
The blog posts on [Multi-Armed Bandits](/autotune/multi-armed-bandit) and [Contextual Bandits](https://www.statsig.com/blog/statsig-autotune-contextual-bandits-personalization) go into depth on use cases and considerations.
| | A/B/n Test | Multi-Armed Bandit (Autotune) | Contextual Bandit (Autotune AI) | Ranking Engine |
| -------------------------- | ---------- | ----------------------------- | -------------------------------- | ---------------------------------------- |
| Typical # Variants | 2-3 | 4-8 | 4-8 | Arbitrary # |
| Personalization Factor | None | None | Moderate | High |
| Input Data Required | None | Very Little (100+ samples) | Little - generally 1000+ samples | Tens of thousands to millions of samples |
| Model Efficacy | None | Basic | Moderate | High |
| Identifies Best Variant | Yes | Yes | No | No |
| Consistent User Assignment | Yes | No | No | No |
## Implementing Autotune
Implementing an Autotune requires checking an experiment in Statsig. After initialization, or on server SDKs, this comes with sub-millisecond latency.
Autotune has a JSON config associated with each variant. The SDK returns this config, which you can use to modify elements of your webpage (for example, an image URL or button color) or to identify which variant is active so you know which code to run.
## When to use Autotune
Autotune has two major differences from A/B testing (Statsig Experiments):
1. The traffic split isn't fixed over the duration of the test. This allows Autotune to divert more traffic to the winner and less to losers while making fewer mistakes. However, the user experience may not be consistent upon repeated visits.
2. Autotune can only optimize for a single metric. Autotune can't accurately measure a collection of metrics, and isn't a reliable way to understand secondary effects of your changes. It works best when the metric is well-understood and has a direct, immediate relationship to the change being tested.
Because of these differences, Statsig recommends Autotune in the following scenarios:
1. The cost of exposing users to a losing treatment is high. For example, sending new users to an inferior landing page may result in lost revenue or churn. Testing two registration flows may cause some users to never sign up. Autotune avoids permanently losing users because it adapts quickly to feedback, unlike a static A/B test.
2. You want the decision to be automated. Because Autotune automatically selects the winner, no human decision-making is required. This is well-suited for launching many simultaneous tests or running a long-term unmonitored test.
3. It's acceptable for users to see different experiences upon return visits. For example, when changing text or recommendation algorithms.
4. You have one simple metric to optimize for (for example, click-through rate) with an immediate effect on the test.
5. You want to test multiple variations. Autotune quickly eliminates poor performers while focusing traffic on the best variants.
Autotune should be avoided in the following scenarios:
1. When you have a complex ecosystem and want to understand secondary effects, tradeoffs between variants, and user behavior.
2. When you are optimizing for complex metrics or delayed effects.
For these cases, use A/B testing with [Experiments](/experiments/overview). In general, it's also a best practice to run Autotune within an experiment with a small holdback group that doesn't receive the Autotune, so you can measure the impact of the Autotune.
================================================================================
# Get Started With Autotune
URL: https://docs.statsig.com/autotune/setup
Updated: 2026-07-11
================================================================================
## How to set up Autotune
1. To create a new Autotune experiment, navigate to the [Autotune section on the Statsig console](https://console.statsig.com/autotune).
2. Click the Create button and enter the name and description of the Autotune experiment that you want to create.
3. Provide the variants that you want to test in the Autotune experiment. Each variant needs a name, and a corresponding JSON value. The variant listed as Control/Default is returned when the Autotune experiment isn't running.
{% figure %}

{% /figure %}
4. Select the success event to optimize for as shown below. You can further specify an optional [event value](/guides/logging-events).
{% figure %}

{% /figure %}
There are a few parameters you can specify:
* Exploration Window: The initial time period where Autotune equally splits traffic. This is useful for noisy or temporal metrics where hourly data swings can bias Autotune's initial measurements.
* Attribution Window: The maximum duration between the exposure and success event that counts as a success. 1 hour is recommended for most applications; adjust if you expect the success event to lag the exposure event by several hours.
* Winner Threshold: The "probability of best" threshold a variant must reach for Autotune to declare it the winner, stop collecting data, and direct all traffic. A lower value results in faster decisions but increases the probability of selecting the wrong winner.
Click **Create** to finalize the setup.
6. As with Feature Gates and Experiments, Statsig provides a code snippet for the exposure check event to add to your code. Select **Start** when you're ready to launch your Autotune test.
{% figure %}

{% /figure %}
## Schedule the start time
Instead of launching an Autotune right away, you can schedule it to start at a specific time. This is useful when you want a test to begin during a particular window, such as after a holiday, without having to start it manually.
To schedule the start:
1. Select **Start** to open the start dialog.
2. Turn on **Schedule Autotune Start Time**.
3. Choose the date and time you want the Autotune to begin. You can schedule a start up to one year in advance, and times snap to the nearest 15-minute interval. The picker uses your local time zone.
4. Select **Schedule Start**.
The Autotune shows a Scheduled status until the start time, when it launches automatically. To change or remove a scheduled start before it begins, use the **Edit Scheduled Start** or **Cancel Scheduled Start** option on the Autotune.
================================================================================
# Autotune
URL: https://docs.statsig.com/autotune/monitoring
Updated: 2026-07-11
================================================================================
## How to monitor your Autotune test
### Computations and traffic allocation
The results tab within Autotune provides a view of your ongoing and completed Autotune tests. Statsig computes Autotune hourly, with metrics and traffic allocation updated throughout the day. Statsig ingests results from exposures after the attribution window is complete. For example, an exposure at 1pm with a 6-hour attribution window isn't counted until 7pm.
### Result
{% figure %}

{% /figure %}
This section shows a summary of your Autotune test. The top bars show a 95% Bayesian Credible interval for the estimated conversion rate (exposure to success event). There's a 95% chance that the real value is within this interval. The table shows the number of exposures, success events, and overall success rate for each variation across the duration of the test, along with a plain-language description of the current test state.
### Details
{% figure %}

{% /figure %}
There are several charts provided:
1. Probability of Best - shows the progress of the Autotune test, and which variant is currently winning.
2. Cumulative success rate - shows the overall success rate (exposure -> success) to date.
3. Daily success rate - shows the success rate for each variation per day.
4. Traffic allocation - shows the daily number of exposures allocated to each variation on a given day.
### Linked experiments
The most reliable way to evaluate whether a bandit is working is to measure whether it drives more of the targeted behavior compared to a baseline experience. You can set up and link an A/B test in Statsig to evaluate this, which also lets you monitor other user behaviors and guardrail metrics.
This is the gold standard of measurement and is highly encouraged.
Standard practice is to wrap the autotune in an experiment with a binary parameter, either as 50/50 or a 90/10 holdback. You can link the experiment to the autotune to get the results on the autotune page. In code, this might look like:
```
experiment_value = statsig.get_experiment('wrapping_experiment').get('flag')
default_param = '..."
if(experiment_value):
param = statsig.get_experiment('autotune').get('param_name')
else:
param = default_param
# use param in code
```
You would start this experiment at the same time that you launch your autotune.
================================================================================
# Methodology
URL: https://docs.statsig.com/autotune/multi-armed-bandit
Updated: 2026-07-11
================================================================================
## Model
The base Autotune implementation uses a Thompson Sampling (Bayesian) algorithm to estimate each variant's probability of being the best variant and allocate a proportional amount of traffic.
For example, if a given variant has a 60% probability of being the best, Autotune allocates 60% of the traffic to it. The multi-armed bandit algorithm adds more users to a treatment as soon as it determines that treatment is clearly better at maximizing the reward (the target metric).
Throughout the process, higher-performing treatments receive more traffic and underperforming treatments receive less. When the winning treatment beats the second-best treatment by a specified margin, the process ends.
Some helpful references:
* [Statsig Blog](https://www.statsig.com/blog/introducing-autotune)
* [Goyal and Agrawal (Microsoft Research)](https://proceedings.mlr.press/v23/agrawal12/agrawal12.pdf) Regret Analysis
* [Doordash Engineering](https://doordash.engineering/2022/03/15/using-a-multi-armed-bandit-with-thompson-sampling-to-identify-responsive-dashers/) Summary Blog
## Advantages
The main advantage of the base Multi-Armed Bandit over a contextual bandit is its ability to converge and identify the best variant. When a single solution works well for all users, the Multi-Armed Bandit efficiently allocates traffic and determines the correct long-term solution while minimizing regret (the cost of exposing many users to a worse variant, as happens in an A/B test).
## Disadvantages
The main disadvantage of a Multi-Armed Bandit compared to a contextual bandit is its inability to personalize. When user attributes interact with variants, Autotune can identify a global maximum that is worse than serving each user their individual best variant.
For example, even if the "US Flag" variant had the highest overall CTR, it would be a poor choice for Canadian users. In such cases, both groups converge to a sub-optimal variant.
| | A/B/n Test | Multi-Armed Bandit (Autotune) | Contextual Bandit (Autotune AI) | Ranking Engine |
| -------------------------- | ---------- | ----------------------------- | -------------------------------- | ---------------------------------------- |
| Typical # Variants | 2-3 | 4-8 | 4-8 | Arbitrary # |
| Personalization Factor | None | None | Moderate | High |
| Input Data Required | None | Very Little (100+ samples) | Little - generally 1000+ samples | Tens of thousands to millions of samples |
| Model Efficacy | None | Basic | Moderate | High |
| Identifies Best Variant | Yes | Yes | No | No |
| Consistent User Assignment | Yes | No | No | No |
================================================================================
# Bandit FAQs
URL: https://docs.statsig.com/autotune/bandit-faq
Updated: 2026-07-11
================================================================================
### When should I see data show up in a bandit?
Diagnostic data appears in near-real time in the logstream on your bandit.
Data in the models/results section depends on your settings. Statsig doesn't count units as enrolled until their attribution window has elapsed. For example, if you have a 24-hour attribution window, it takes 24 hours to see your first units and model updates. Statsig trains models hourly with a few minutes of data delay, so there may be 1 to 2 additional hours of delay beyond that period.
This operation doesn't have a strict SLA on Statsig's side. Delays may occur due to data quality checks or other investigations to prevent bad model training data from affecting your production environment.
### What do I put into the Variant JSON?
The variants return this JSON as a config, similar to dynamic configs or experiments. You can put an identifier for the variant, or specify a set of attributes that will be accessed in code to avoid writing conditional statements to fetch the corresponding attributes.
### Why use a linked experiment?
Bandits optimize for a single target metric or event, which alone doesn't provide a detailed analysis of how the bandit affects your users. Wrapping a bandit in an experiment (where the control group receives a default experience and the variant group receives the bandit's experience) lets you get deeper insights on its impact on end users. This step is highly recommended.
### Where is my Autotune data available?
Statsig downloads Autotune data hourly into the `statsig_first_exposures` metric source. Select the history modal on the autotune to view the SQL and tables generated for an individual autotune.
### Why was a "losing" variant chosen?
Autotune assumes that a metric is consistent over time. A metric with strong seasonality (for example, a metric that drops on weekends) isn't a good candidate for an Autotune experiment. With seasonality, the following situation is possible:
* A variant that consistently performs worse than others receives less and less traffic until it has essentially zero traffic.
* Other variants, still receiving traffic, see their metric drop when seasonal effects occur.
* The variant with zero traffic doesn't see that metric drop (because no traffic is allocated to it), which causes Statsig to select it as the winning variant.
This can sometimes cause Statsig to choose a "losing" variant as the winner.
================================================================================
# Advanced Bandit Usage
URL: https://docs.statsig.com/autotune/using-bandits
Updated: 2026-07-11
================================================================================
Both contextual and non-contextual bandits are managed on Statsig's console, or through Statsig's [console API](/console-api/introduction) for programmatic creation. Both bandit types use a common, streamlined API, so you can explore either use case without significant changes from using experiments.
Statsig's bandits provide the power of complex bandits through a simple interface. A key observation from development is that many such systems fail due to implementation issues.
No additional steps beyond a regular experiment check are required to use a bandit. For contextual multi-armed bandits, attach any context to your user object (usually at initialization) before making the experiment check. You can confirm this in the diagnostics tab of your bandit in the console.
### Checking a bandit
Check bandits using your standard experiment call. For example, in React, configure a bandit's variant JSON like this:
```
{
"text": "",
...
}
```
and accessing it in code using the same pattern as [experiments](/guides/abn-tests):
```
const banditText = useExperiment('contextual_bandit').config.get('text');
```
This is separated from a bandit's linked experiment call to reduce excessive logging; if you have a linked wrapping experiment, wrap the call after checking the experiment.
Statsig will automatically detect and differentiate categorical and numerical features based on cardinality.
### Getting a list
Getting a ranked list of variants is a common requirement. Client-side filtering or other constraints may prevent showing a particular variant.
Statsig supports getting a list of scored outcomes, but requires manual exposure logging so that Statsig knows which variant you displayed to the user.
### Outcomes
In Statsig Cloud, log an outcome event (as a flag, or with a value) to Statsig, and Statsig handles connecting exposures to outcome events within your configured time periods.
In Statsig Warehouse Native, you can choose which approach to take. Logging events with Statsig exports events to your warehouse in near-real-time for use in Bandits, or you can provide your own outcome dataset as a metric source.
{% callout type="note" %}
Data latency matters: if your output dataset has more than 24 hours of lag, bandit exploration can slow down significantly.
{% /callout %}
================================================================================
# Switchback V2
URL: https://docs.statsig.com/experiments/types/switchback-v2
Updated: 2026-07-11
================================================================================
## What is a switchback experiment?
A **switchback experiment** tests two versions of a system by **alternating them over time**. This methodology is useful when it isn't possible to isolate user experiences between treatment and control groups.
For example, on a rideshare platform, offering lower prices to a treatment group might increase demand for cars and indirectly affect control riders. A switchback experiment measures impact over time by alternating between experiences instead of splitting users.
## How switchback experiments work
At a high level, switchback experiments work as follows:
1. **Define clusters and their schedule:** A **cluster** is a group of users that switch between experiences on the same cadence.
*Example:* All users in **New York** and **Chicago** follow this schedule:
* **9:00–10:00 AM:** Control
* **10:00–11:00 AM:** Treatment
* **11:00 AM–12:00 PM:** Control
* **12:00–1:00 PM:** Treatment
2. **Aggregate data by time buckets:** A bucket represents a single window of time during which the user experience remains constant. Each bucket is treated as one data point in the analysis.
*Example:* In the schedule above, the experiment produces **four buckets**: two for control and two for treatment.
* **Control**
* Bucket 1: 9:00–10:00 AM
* Bucket 3: 11:00 AM–12:00 PM
* **Treatment**
* Bucket 2: 10:00–11:00 AM
* Bucket 4: 12:00–1:00 PM
3. **Run regression analysis:** Statistical models account for factors such as time of day, day of week, or cluster attributes when estimating the difference between treatment and control.
4. **Compare results:** The final output resembles a traditional A/B test, including metrics such as estimated lift and confidence intervals.
## Setting up a switchback experiment
Defining the hypothesis, metrics, groups, targeting, and parameters follows the same general workflow as a traditional A/B test. Switchback experiments add three additional configurations: **clusters**, **scheduling**, and **analysis configuration**.
## Defining cluster(s)
Clusters are groups of users who follow the same experience cadence. In traditional A/B tests, the selected ID Type acts as both the randomization unit and the unit at which metrics are calculated. In a switchback experiment, however, the ID Type defines the unit for metric calculation, while clusters determine which experience a user receives over time.
Statsig provides three ways to define a cluster.
| Method | Description | Inputs |
| :----- | :------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Single | Single Cluster where all users eligible for the experiment follows the same cadence | **Start With:** defines which experience (control or treatment) starts the switchback |
| Auto | Provides a two-cluster configuration where Statsig automatically assigns users to each cluster based on the specified inputs. | **Cluster ID Type:** Select a custom ID from the Exposure User Object that Statsig uses to split users into clusters. For example, if server\_id is present on the user object, Statsig randomly assigns server\_id values to each cluster, and groups users based on their server\_id. |
| Manual | Two-cluster configuration where users are manually assigned to each cluster. | **Cluster Field:** Select a field from the Exposure User Object that Statsig uses to assign users to clusters. For example, if Country is selected as the Cluster Field, you can assign specific countries to either Cluster 1 or Cluster 2. Statsig then places users into clusters based on the value of that field (for example, their country). |
Fields used as Cluster ID Type (Auto) and Cluster Field (Manual) can be used as covariates in the regression analysis and are available to break down metric data in the results section.
## Defining scheduling
Once the clusters are configured, you can define the schedule for experiences within each cluster.
{% figure %}

{% /figure %}
### Inputs
**Window Size / Unit:** The length of each window during which a user’s experience remains constant.
**Experiment Start Date / Time / Timezone:** The date and time when the switchback experiment begins. All clusters start simultaneously. The experiment can't be started if the selected start date or time is in the past when the experiment starts.
**Target Duration:** The intended length of time the experiment should run. By default, the experiment doesn't automatically stop when this duration is reached. Users continue to receive the switched experiences according to the configured schedule. If **”Stop Experiment at target duration”** is enabled, the experiment stops automatically at the end of the specified duration. After that, users are served the default experiment value configured in code.
## Define analysis configuration
You can configure how exposures and metrics are handled during the transition periods between switchback windows. For example, if a rideshare marketplace switches from Control to Treatment at 9 AM, the system may still experience lingering effects from the Control period, such as drivers already on active trips or riders remaining in the queue from earlier periods. In these cases, you may want to exclude exposures and metric data recorded shortly after the switch.
Configure **Burn-in** and **Burn-out** periods in the **Analysis Configuration** section to exclude these transition exposures.
{% figure %}

{% /figure %}
### Inputs
**Burn-in Period:** The amount of time at the beginning of each window that is excluded from analysis.
**Burn-out Period:** The amount of time at the end of each window that is excluded from analysis.
**Metric Calculation:** Determines how metric events are attributed to an exposure. The following options define how metrics are aggregated within each switchback window.
{% figure %}

{% /figure %}
* **Period from first exposure:** Aggregates metric data for a specified period of time after the user’s first exposure.
* **Entire window:** Aggregates metric data across the full switchback window.
* **Period between burn-in and burn-out:** Aggregates metric data only within the portion of the window between the burn-in and burn-out periods.
**Exposure Calculation:** Defines how exposures logged during switchback windows are handled in the analysis. The following options are available:
{% figure %}

{% /figure %}
* **Include exposures in burn periods:** Considers all exposures recorded during the switchback window, including those that occur within the burn-in and burn-out periods.
* **Exclude exposures in burn periods:** Considers only exposures recorded between the burn-in and burn-out periods.
**\[Coming soon] Specify Pre-computed User Dimensions:** Configure user dimensions that can be used to break down experiment results in the results section. These fields are selected from the exposure user properties or entity properties.
================================================================================
# Bonferroni Correction
URL: https://docs.statsig.com/experiments/statistical-methods/methodologies/bonferroni-correction
Updated: 2026-07-11
================================================================================
## What is Bonferroni correction?
A Bonferroni Correction is a statistical method that reduces the probability of false positives by adjusting the significance level for multiple comparisons.
If you run a test with α = 0.05, the probability of a false positive is 5%. Running more comparisons at the same significance level increases the chance of at least one false positive, because each comparison is an additional opportunity for a false positive.
Bonferroni correction is an optional feature on Statsig experiments. Statsig divides the significance level (α) by the number of comparisons being evaluated.
You can choose to apply these based on one or both of the following:
* The number of test groups (multiple treatment hypotheses). Statsig divides the significance level by the number of variants being compared against control.
* The number of metrics in the scorecard. Here you may select what percentage of your total α is divided evenly among the Primary Metrics, and the remaining α is split equally among Secondary Metrics. For example:
* Significance level of 0.05
* 2 Primary Metrics and 4 Secondary Metrics
* 60% of α applied to Primary Metrics
* Each Primary Metric is calculated with α = 0.6 \* 0.05 / 2 = 0.015
* Each Secondary Metric is calculated with α = 0.4 \* 0.05 / 4 = 0.005
* If both corrections are selected, Statsig applies them together. In the example above, to also correct for having 2 test groups, divide each α by 2.
When analyzing dimensions, if correction for metrics is enabled, Statsig applies the correction separately for the dimensional breakdown. Statsig uses the number of dimensions as the total metric count for the dimensional analysis, but this doesn't impact topline metrics.
{% figure %}

{% /figure %}
## How Bonferroni correction affects experiment metrics
In the experiment scorecard section, Statsig derives confidence intervals for applicable metrics from (1 - adjusted α). Hovering over a confidence interval displays the adjusted α alongside other relevant metric details.
In the experiment explore section, Statsig calculates a new adjusted α based on your selections, and the confidence intervals use (1 - adjusted α).
================================================================================
# Benjamini–Hochberg Procedure
URL: https://docs.statsig.com/experiments/statistical-methods/methodologies/benjamini-hochberg-procedure
Updated: 2026-07-11
================================================================================
## What is the Benjamini-Hochberg procedure?
The Benjamini-Hochberg Procedure ("BH" procedure) is a statistical method that reduces the probability of false positives by adjusting the significance level for multiple comparisons. BH is less strict than a [Bonferroni Correction](/experiments/statistical-methods/methodologies/bonferroni-correction): instead of controlling the chance of at least one false positive (Family Wise Error Rate), BH controls the expected proportion of false positives when the null hypothesis has been rejected (False Discovery Rate).
You can enable the BH procedure for individual experiments, or configure global Experiment Settings to use it by default.
{% figure %}

{% /figure %}
## How BH adjusts the significance level
The [Benjamini-Hochberg Procedure](https://www.statisticshowto.com/benjamini-hochberg-procedure/) updates the significance level (modifying your pre-set $\alpha$). Statsig calculates the new significance level by sorting metric p-values in ascending order and comparing each with a paired threshold. Each p-value’s paired threshold is the desired False Discovery Rate divided by the number of comparisons, multiplied by the rank of that p-value in the ordered list. The largest threshold value higher than its corresponding p-value becomes the new significance level ($\alpha$).
You can apply the Benjamini-Hochberg Procedure based on:
* The number of test groups (multiple treatment hypotheses). For each metric, aggregate the list of p-values from each variant and complete the Benjamini-Hochberg procedure.
* The number of metrics in the scorecard. For each variant, aggregate the list of p-values from each metric and complete the Benjamini-Hochberg procedure.
* Both the number of test groups and number of metrics in the scorecard. All p-values are aggregated to complete the Benjamini-Hochberg procedure.
Statsig doesn't apply BH procedure when evaluating the p-values of any event-dimension or user-property experiment metric results. Only the top-line metric results are compared to the new significance level.
## How experiment metrics appear after applying BH
In the experiment scorecard section, Statsig derives confidence intervals for applicable metrics from (1 - adjusted α). Hovering over a confidence interval displays the adjusted α alongside other relevant metric details.
In the experiment explore section, Statsig calculates a new adjusted α based on your selections, and the confidence intervals use (1 - adjusted α).
================================================================================
# SRM Checks
URL: https://docs.statsig.com/stats-engine/methodologies/srm-checks
Updated: 2026-07-11
================================================================================
## SRM - sample ratio mismatch
Sample ratio mismatch (SRM) occurs when the observed allocation of **unique** users between test groups differs from the expected allocation or split of the test. Refer to the [Statsig blog post on sample ratio mismatch](https://www.statsig.com/blog/sample-ratio-mismatch) for background.
SRM is a signal that there may be unknown bias in the test. This is a significant problem because, without a clear diagnosis of the imbalance, it is difficult to know how much the bias affects your results.
## SRM checks
Statsig runs SRM checks on all experiments and feature gates as part of Health Checks (refer to [Health Checks](/experiments/monitor)). Statsig uses a Chi-squared test to identify whether the split of users between groups indicates a sample ratio mismatch.
{% figure %}

{% /figure %}
Statsig automatically analyzes data by common dimensions logged by the Statsig SDK to identify potential drivers of SRM. These include sdk\_type, sdk\_version, reason, is\_bot, browser\_name, browser\_version, os, os\_version, and region to identify potential causes.
{% figure %}

{% /figure %}
================================================================================
# Pre-Experiment Bias
URL: https://docs.statsig.com/experiments/statistical-methods/pre-experiment-bias
Updated: 2026-07-11
================================================================================
In some cases, users in two experiment groups can have meaningfully different average behaviors before your experiment applies any intervention to them. If this difference persists after your experiment starts, experiment analysis may attribute that pre-existing difference to your intervention, making a result appear more or less significant than it is.
[CUPED](/experiments/statistical-methods/methodologies/cuped) helps address this bias, but can't fully account for it. Some metrics, such as retention, aren't suitable candidates for CUPED and can't be easily adjusted.
Statsig proactively measures the pre-experiment values of all scorecard metrics for all experiment groups, and determines whether the values are significantly different and could cause misinterpretations. If Statsig detects bias, it notifies users and places a warning on relevant Pulse results.
### How it works
Statsig provides a "Days Since Exposure" view to help identify novelty effects and pre-experiment effects. For example, the test group in the experiment below had a consistently higher mean than the control group in the week before exposure for this metric.
{% figure %}

{% /figure %}
Statsig detects this bias by running the standard [Pulse](/experiments/interpreting-results/read-results) calculation on the pre-experiment term (looking back one week in Cloud, and your configured CUPED lookback window in Warehouse Native), and calculating the p-value for the null hypothesis that the groups are identical. Statsig flags relevant results according to logic that balances awareness and false positives stemming from high numbers of scorecard metrics or groups.
### What to do
Pre-experiment bias can occur by chance and is not always a major issue.
* If the total delta is small, it may not meaningfully influence your interpretation of results.
* If CUPED can account for the bias, the bias shouldn't affect your results.
In many cases, the warning is informational and you can proceed while treating impacted metrics with caution. This is often appropriate if the metric isn't critical to the experiment, or if you care more about the directional movement than the exact number. Additional time may also reduce the bias when there is no systemic source, as new users dilute the initial imbalance.
If the metric is critical to your analysis and you need the exact numerical value, consider resalting and restarting the experiment.
================================================================================
# Managing SRM
URL: https://docs.statsig.com/experiments/monitoring/srm
Updated: 2026-07-11
================================================================================
## What is SRM
SRM, or sample ratio mismatch, is a problem with experiments characterized by too many units in some groups and too few in others.
The example below is an exposure crosstab of an experiment with SRM. Even though the group percentages may look similar, if an assignment system is splitting traffic evenly, an imbalance this extreme or greater would have less than a 0.01% chance of occurring randomly.
{% figure %}

{% /figure %}
Statsig and most experiment platforms normalize metrics per-user: a count metric is measured as total count divided by unique users in the experiment. In isolation, having more users in one group isn't a problem. However, SRM is problematic for the following reason.
## Why SRM is an issue
SRM is an issue because it is usually *non-random*: the extra or missing traffic is *not identical* to the original traffic. Common causes of SRM include:
* A bug causes a user's client or browser to crash before an exposure log can be sent. Users who don't return aren't re-exposed, but users who return are included. This introduces bias in measurement.
* A conditional dependency filters who is exposed based on some characteristic for one or more groups, making those groups non-identical to other groups and biasing measurement.
* A script bulk-exposes users one group at a time, and logs are truncated after a certain count, causing the last group's exposures to be truncated.
SRM checks are critical because they detect these effects even at low rates. Even low-rate SRM can lead to serious inaccuracy in experiment readouts.
## How SRM is detected
Statsig detects SRM using a [chi-squared test](https://en.wikipedia.org/wiki/Chi-squared_test), which analyzes categorical data to determine whether observed frequencies match expected frequencies. For example, in the experiment above, the expected distribution is 167.85k units per group, but the observed distribution is [166.08k, 171.18k, 166.30k].
If the p-value of the test is low, the null hypothesis that the groups are identical is rejected, and the result indicates a difference between the groups' observed and expected assignment rates.
## What to do if an experiment has SRM
On Statsig, SRM creates a warning or failure state on an experiment's health check when detected, depending on how extreme the SRM is.
{% figure %}

{% /figure %}
This often causes concern: teams don't want to reset their experiment and lose collected data, and if there *is* an underlying issue it may reproduce after a reset.
Follow these steps to diagnose and address SRM:
**1.) Check the time series data**
Statsig generates a chart of SRM p-value over time. If the chart is noisy and bounces around, the alert is more likely a false positive. If it consistently trends down to 0, there is likely a real assignment issue.
The following is an example of a p-value chart that indicates a real issue.
{% figure %}

{% /figure %}
**2.) Understand if there's a clear root cause for SRM**
Use Statsig's SRM debugger or analyze exported exposures to determine whether a specific segment is driving SRM. Often a bug is isolated to one platform such as Android, or restricted to users with low internet speeds. If you find and fix the bug, you can restart the experiment safely.
If the issue is clearly isolated, filtering out that segment from analysis is also reasonable when the experiment was expensive or required a long data collection period.
At this point, you should have a reasonable sense of whether there is a real issue. Use that assessment to decide next steps.
**3.) Assess options**
In many cases, the best path is to investigate, fix, and restart. In some cases, the SRM may be mild enough, and the experiment low-risk enough, that making a decision with the affected data is acceptable.
Statsig strongly recommends against proceeding without investigation and considers restarting the experiment a best practice, ideally after investigating any potential SRM cause.
================================================================================
# Variance Reduction
URL: https://docs.statsig.com/experiments/statistical-methods/variance-reduction
Updated: 2026-07-11
================================================================================
## Variance reduction
[Variance](/experiments/statistical-methods/variance) measures the amount of noise in a metric or experiment results. Higher variance produces larger confidence intervals and requires more sample size to consistently observe a statistically significant result for the same effect size.
Reducing variance shortens experiment run times by requiring less sample. Statsig uses a form of CUPED based on a [2013 Microsoft paper](https://www.exp-platform.com/Documents/2013-02-CUPED-ImprovingSensitivityOfControlledExperiments.pdf) (Deng, Xu, Kohavi, & Walker). Statsig automatically applies CUPED to experiments and runs it for the topline results on key metrics in Pulse. This produces significant variance reduction for the large majority of metrics where CUPED can be applied.
For more details, refer to the [launch post for CUPED](https://blog.statsig.com/cuped-on-statsig-d57f23122d0e).
## CUPED - Controlled-experiment Using Pre-Existing Data
CUPED (short for Controlled-experiment Using Pre-Existing Data) is a technique that uses user information from before an experiment to reduce variance and increase confidence in experimental metrics. At Statsig, this pre-experiment data covers the 7 days before each user's exposure, rather than a fixed window before the experiment starts for all users. This helps reduce bias in experiments where groups were randomly different before any treatment applied.
The Cloud product uses stratification alongside CUPED to account for users who may not have pre-experiment data. Statsig groups users into strata based on available pre-experimentation information. Statsig first estimates treatment and control effects within each stratum, then aggregates them to produce an overall result. Statsig then applies the standard difference-in-means and variance estimation. This approach retains users with missing pre-experiment data while still providing variance reduction where applicable.
## Winsorization
Winsorization is another technique for reducing noise by managing the influence of outliers.
Winsorization measures the percentile *Px* of a metric and sets all values above *Px* to *Px*. This reduces the influence of extreme outliers caused by factors such as logging errors or bad actors.
## Metric selection
The metrics you use can significantly influence the sensitivity of your analysis. The transformations described above, combined with techniques such as creating threshold-based flags, allow you to trade exact numbers for more statistical power. For more information, refer to the [blog post on understanding and reducing variance](https://www.statsig.com/blog/understanding-and-reducing-variance-and-standard-deviation).
## Related resources
* [Deng, Xu, Kohavi, & Walker](https://exp-platform.com/Documents/2013-02-CUPED-ImprovingSensitivityOfControlledExperiments.pdf): seminal paper on using CUPED for online controlled experiments
* [Booking.com: CUPED in practice](https://booking.ai/how-booking-com-increases-the-power-of-online-experiments-with-cuped-995d186fff1d): blog post on the theory and practice of CUPED
* [Improving the Sensitivity of Online Controlled Experiments: Case Studies at Netflix](https://www.kdd.org/kdd2016/papers/files/adp0945-xieA.pdf)
================================================================================
# CUPED
URL: https://docs.statsig.com/experiments/statistical-methods/methodologies/cuped
Updated: 2026-07-11
================================================================================
## CUPED - Controlled-experiment Using Pre-Existing Data
CUPED (short for Controlled-experiment Using Pre-Existing Data) is a technique that uses user information from before an experiment to reduce variance and increase confidence in experimental metrics. At Statsig, this pre-experiment data is defined as the 7 days before each user's exposure rather than a fixed window before the experiment starts for all users. This helps debias experiments that have meaningful pre-exposure bias (for example, groups that were randomly different before any treatment was applied).
The Cloud product uses a 7-day window for CUPED calculation. For Warehouse Native customers, Statsig recommends a 7-day window, but you can customize it to any length.
For more details, refer to the [Variance Reduction](/experiments/statistical-methods/variance-reduction) page.
For an in-depth look at the methodology, refer to CURE by Statsig.
## CUPED for simple aggregations
The methodology for simple aggregations is described in the original [Microsoft paper](https://www.exp-platform.com/Documents/2013-02-CUPED-ImprovingSensitivityOfControlledExperiments.pdf), as well as the in-depth [article](https://www.statsig.com/blog/cuped) on the technique.
The Cloud product uses stratification alongside CUPED to account for users who may not have pre-experiment data. Statsig groups users into strata based on available pre-experimentation information, estimates treatment and control effects within each stratum, then aggregates them to produce an overall result. Statsig then applies the standard difference-in-means and variance estimation. This approach lets Statsig retain users with missing pre-data while still benefiting from variance reduction where applicable.
## CUPED for ratio metrics
The Microsoft paper also gives details on how to implement CUPED for those with a different analysis unit (Appendix B). On Statsig, this extends to ratio metrics, where each experiment unit is represented by a numerator and a denominator. The variance reduction process finds the variance of experiment data, pre-experiment data, and the covariance between the two.
Denote the numerator, denominator, pre-experiment numerator, and pre-experiment denominator of a unit as $Y$, $N$, $X$, and $M$, respectively. Using the CUPED-reduced variance formula,
$$
Var(\frac\{Y\_\{cv}}\{N\_\{cv}})=Var(\frac\{Y}\{N})+\theta^2 Var(\frac\{X}\{M})-2\theta Cov(\frac\{Y}\{N}, \frac\{X}\{M})
$$
where optimal $\theta$ is found as
$$
\frac\{Cov(\frac\{Y}\{N}, \frac\{X}\{M})}\{Var(\frac\{X}\{M})}
$$
expanded to
\\
$$
\frac\{Cov(\frac\{Y}\{\mu\_N}-\frac\{\mu\_Y N}\{\mu^2\_N}, \frac\{X}\{\mu\_M}-\frac\{\mu\_X M}\{\mu^2\_M})}\{Var(\frac\{X}\{\mu\_M}-\frac\{\mu\_X M}\{\mu^2\_M})}
$$
This gives:
$$
\frac\{\hat\{Y\_\{c}}}\{\hat\{N\_\{c}}}=\frac\{Y\_\{c}}\{N\_\{c}}-\theta( \frac\{X\_\{c}}\{M\_\{c}} - \mathbb\{E}\[R])
$$
$$
\frac\{\hat\{Y\_\{t}}}\{\hat\{N\_\{t}}}=\frac\{Y\_\{t}}\{N\_\{t}}-\theta( \frac\{X\_\{t}}\{M\_\{t}} - \mathbb\{E}\[R])
$$
Because $\mathbb\{E}\[R]$ is hard to derive, the expectation term is the same for both groups. Substituting $\mathbb\{E}\[R]$ with $\frac\{X\_\{c}}\{M\_\{c}}$ transforms the formulas above to the following two:
$$
\frac\{Y\_\{cv}(control)}\{N\_\{cv}(control)}=\frac\{Y(control)}\{N(control)}
$$
$$
\frac\{Y\_\{cv}(test)}\{N\_\{cv}(test)} \\\\
:=\frac\{Y(control)}\{N(control)} - (\frac\{Y(control)}\{N(control)} - \theta \frac\{X(control)}\{M(control)}) + (\frac\{Y(test)}\{N(test)} - \theta\frac\{X(test)}\{M(test)}) \\\\
:=\frac\{Y(test)}\{N(test)} - \theta\frac\{X(test)}\{M(test)} + \theta \frac\{X(control)}\{M(control)}
$$
Using the optimal $\theta$, Statsig reduces group-level variance by applying the parameter to calculate the adjustment. Across-group $\theta$ doesn't necessarily reduce variance for one group, or the sum of variances of all groups, but in most cases it does. Simulations show that 98.3% of metrics saw a decrease through CUPED.
Statsig uses CUPED variance when all of the following are met:
* Core assumptions of the CUPED model are satisfied; rounding error or other data artifacts can violate this
* E(X\_hat) = E(X)
* The pooled variance of the adjusted population across groups is \< the variance of the unadjusted population
* Enough units have pre-experiment values (> 100)
* Enough percentage of units have pre-experiment values (> 5%)
================================================================================
# Winsorization
URL: https://docs.statsig.com/experiments/statistical-methods/methodologies/winsorization
Updated: 2026-07-11
================================================================================
Winsorization is a technique for reducing noise in experiment results caused by outliers.
Winsorization measures the percentile Px of a metric and sets all values above Px to Px.
Statsig computes the Px value using all non-zero and non-null unit-level values of the metric. Metrics are aggregated from rows or events, and the Px'th unit's value is used as the threshold to adjust other units' values.
{% tabs %}
{% tab title="Statsig Cloud" %}
At Statsig, the default percentile for winsorization is 99.9%. This reduces the influence of extreme outliers caused by factors such as logging errors or bad actors.
Statsig applies winsorization to sum, event count, mean, ratio, and funnel metrics, including imported metrics. Winsorization doesn't apply to Participation or User Accounting metrics.
{% /tab %}
{% tab title="Warehouse Native" %}
Statsig Warehouse Native lets you configure winsorization per metric, and choose the upper and/or lower bounds to apply.
{% figure %}

{% /figure %}
Statsig applies winsorization to sum, event count, mean, and ratio metrics.
{% /tab %}
{% /tabs %}
## Metric capping
Metric capping is a technique for handling outliers. You can define maximum values for a metric for any unit types configured for that metric. Statsig automatically adjusts any value that exceeds the cap downward to match the cap.
For example, if purchases greater than $10,000 per day shouldn't skew analysis, Statsig adjusts any transaction exceeding that threshold downward to $10,000. Capped metrics are available for Event Count and Aggregation (sum) metric types.
================================================================================
# Confidence Intervals
URL: https://docs.statsig.com/experiments/statistical-methods/confidence-intervals
Updated: 2026-07-11
================================================================================
Confidence intervals quantify the uncertainty in observed metric deltas. A 95% confidence interval contains the true effect 95% of the time. If an experiment ran 100 times, the true value of the metric delta would fall inside the confidence intervals 95 times.
{% figure %}

{% /figure %}
A 95% confidence interval that doesn't contain zero (the green bar above) represents a statistically significant result (with *α = 0.05*). This isn't always the case: there are cases when the p-value of the difference between test and control is statistically significant, but due to uncertainty in the control, a relative delta confidence interval may cross zero (using [The Delta Method](/experiments/statistical-methods/methodologies/delta-method)) or be represented as a point estimate (using [Fieller Intervals](/experiments/statistical-methods/methodologies/fieller-intervals)) while the absolute difference's p-value is statistically significant.
Only 5% of the time would you expect to see the confidence interval exclude zero if the true effect was zero (a false positive). Larger confidence intervals imply less certainty in the exact size of the effect with a larger range of likely values.
## Computing confidence intervals
Statsig calculates confidence intervals using a two-sample z-test. This test requires the variance of the metric delta being measured, which Statsig derives differently depending on the metric type (details at [Variance](/experiments/statistical-methods/variance)).
After establishing the variance of the delta, you can compute the confidence intervals.
### Two-sided tests
For the **absolute metric delta**, the confidence interval is given by:
$$
CI(\Delta \overline\{X}) = \Delta \overline\{X} \pm Z\_\{\alpha/2} \cdot \sqrt\{\{var(\Delta \overline\{X})}}
$$
where:
* $Z\_\{\alpha/2}$ is the z-critical value for the desired significance level (1.96 for the standard $\alpha=0.05$ and 95% confidence interval) for a two-sided test
* $var(\Delta \overline\{X})$ is the variance of the absolute delta (details [here](/experiments/statistical-methods/variance))
The confidence interval for the **relative metric delta** can use one of two methods: [Fieller Intervals](/experiments/statistical-methods/methodologies/fieller-intervals) or the [Delta Method](/experiments/statistical-methods/methodologies/delta-method). You can opt for either method. Statsig enables Fieller Intervals for all new customers by default.
When using Fieller Intervals, compute the relative metric delta CI using:
$$
CI(\\% \Delta \overline\{X} ) = \frac\{1}\{1-g} ( \frac\{\overline\{X\_T}}\{\overline\{X\_C}} - 1 \pm \frac\{Z\_\{\alpha/2}}\{\sqrt\{n\_C} \cdot \overline\{X\_C}} \sqrt\{(1-g) \cdot \frac\{var(X\_T)}\{n\_T(n\_T-1)} + \frac\{\overline\{X\_T} var(X\_C)}\{\overline\{X\_C} n\_C (n\_C-1)}})
$$
When using the Delta Method, the confidence interval is:
$$
\begin\{split}
CI(\Delta \overline X\\%)
&= \Delta \overline X\\% \pm Z\_\{\alpha/2} \cdot\sqrt\{\{var(\Delta \overline X\\%)}}\\\\
&= \frac\{\Delta \overline X}\{\overline X\_c} \pm Z\_\{\alpha/2} \cdot\sqrt\{(\frac\{\overline X\_t}\{\overline X\_c})^\{2} \cdot (\frac\{var(X\_c)}\{n\_c \cdot \overline X\_c^2} + \frac\{var(X\_t)}\{n\_t \cdot \overline X\_t^2})} \cdot 100\\%
\end\{split}
$$
If using the Delta Method and the control mean is not significantly away from zero, then it's simplified to:
$$
\begin\{split}
CI(\Delta \overline X\\%)
&= \Delta \overline X\\% \pm Z\_\{\alpha/2} \cdot\sqrt\{\{var(\Delta \overline X\\%)}} \\\\
&= \frac\{\Delta \overline X}\{\overline X\_c} \pm Z\_\{\alpha/2} \cdot \frac\{\sqrt\{\{var\left(\Delta \overline X\right)}}}\{\overline X\_c} \cdot 100\\%
\end\{split}
$$
### One-sided tests
When running one-sided tests, the confidence interval calculation changes to account for a redistribution of the desired false positive rate when looking for increases or decreases in the metric:
$$
CI(\Delta \overline\{X}) = \begin\{cases}
\left\[\Delta \overline\{X} - Z\_\{\alpha} \cdot \sqrt\{\{var(\Delta \overline\{X})}}, \quad +\infty \right) & \text\{if right-hand test}\\\\
\\\\
\left(-\infty, \quad \Delta \overline\{X} + Z\_\{\alpha} \cdot \sqrt\{\{var(\Delta \overline\{X})}} : \right] & \text\{if left-hand test}
\end\{cases}
$$
where:
* $Z\_\{\alpha}$ is the z-critical value for the desired significance level (1.645 for the standard $\alpha=0.05$ and 95% confidence interval) for a one-sided test
* $var(\Delta \overline\{X})$ is the same as for two-sided tests
* the choice of confidence interval depends on if the one-sided test is looking for increases or decreases in the metric
## Welch's t-test for small sample sizes
For small sample sizes, Statsig uses Welch's t-test instead of a standard z-test. Welch's t-test handles samples of unequal size or variance without increasing the false positive rate. The confidence interval calculation follows the same structure as the two-sample z-test (depending on whether the test is one- or two-sided), replacing the z-critical value with the t-critical value with degrees of freedom $\nu$.
For a two-sided test, the confidence interval is therefore:
$$
CI(\Delta \overline\{X}) = \Delta \overline\{X} \pm t\_\{\alpha/2} \cdot \sqrt\{\{var(\Delta \overline\{X})}}
$$
$$
\nu = \frac\{\left(var(\overline X\_t) + var(\overline X\_c)\right)^2}\{\frac\{var(\overline X\_t)^2}\{N\_t - 1}+\frac\{var(\overline X\_c)^2}\{N\_c - 1}}\
\= \frac\{var(\Delta\overline\{X})^2}\{\frac\{var(\overline X\_t)^2}\{N\_t - 1}+\frac\{var(\overline X\_c)^2}\{N\_c - 1}}
$$
Where $N\_t$ and $N\_c$ are the number of users in the test and control groups, respectively. For a large number of degrees of freedom, the t-statistic converges with the z-statistic. Therefore, Statsig uses Welch's t-test only when $\nu \< 100$.
## Comparing experiment data to a fixed baseline: one-sample t-test
To answer questions such as "Does my test variant lead to a click-through rate higher than 0.5?", define a fixed-baseline comparison when adding metrics to the experiment.
Statsig calculates the confidence interval as:
$$
CI(\Delta \overline X) = (\overline X\_\{group} - fixed \ value) \pm Z \cdot\sqrt\{\{var( \overline X\_\{group})}}
$$
================================================================================
# p-Value Calculation
URL: https://docs.statsig.com/experiments/statistical-methods/p-value
Updated: 2026-07-11
================================================================================
In Null Hypothesis Significance Tests, the p-value is the probability of observing an effect larger than or equal to the measured metric delta, under the assumption that the null hypothesis is true. A p-value below the pre-defined Type I Error threshold ($\alpha$) serves as evidence of a true effect.
The methodology for p-value calculation depends on the number of degrees of freedom ($\nu$). A two-sample z-test is appropriate for most experiments. Statsig uses Welch's t-test for smaller experiments with $\nu \< 100$. In both cases, the p-value depends on the metric [mean](/experiments/statistical-methods/metric-deltas) and [variance](/experiments/statistical-methods/variance) computed for the test and control groups.
Typically, a p-value below the threshold $\alpha$ occurs only when the confidence interval doesn't cross 0. However, an exception can occur in the Statsig UI: when the p-value of the difference between test and control is statistically significant, but uncertainty in the control causes a relative delta confidence interval to cross zero (using [The Delta Method](/experiments/statistical-methods/methodologies/delta-method)) or be represented as a point estimate (using [Fieller Intervals](/experiments/statistical-methods/methodologies/fieller-intervals)), while the absolute difference's p-value is statistically significant.
## Two-sample tests
### Two-sided z-test
You can compute the z-statistic (a.k.a. z-score) of a two-sample z-test in multiple equivalent formats:
$$
\begin\{split}
Z &= \frac\{\overline X\_t - \overline X\_c}\{\sqrt\{var(\overline X\_t)+ var(\overline X\_c)}} \\\\
&= \frac\{\overline X\_t - \overline X\_c}\{\sqrt\{var(\Delta \overline\{X})}} \\\\
&= \frac\{\overline X\_t - \overline X\_c}\{\sqrt\{\sigma\_\{\overline\{X}\_t}^2 + \sigma\_\{\overline\{X}\_c}^2}}
\end\{split}
$$
where:
* $Z$ is the observed z-statistic (not the z-critical value $Z\_\{\alpha/s}$)
* $var(\Delta \overline\{X})$ is the variance of the absolute delta of means
* $var(\overline\{X}\_i)$ is the variance of sample means either control or treatment group (details [here](/experiments/statistical-methods/variance))
* $\sigma\_\{\overline\{X}\_t}$ is the standard error of the mean of either control or treatment group (these are the terms you can find in Pulse under the Statistics tab of a metric)
The two-sided p-value comes from the standard normal cumulative distribution function:
$$
p-value = 2 \cdot \frac\{1}\{\sqrt\{2\pi}} \int \limits \_\{-\infty}^\{-|Z|}\{e^\{-t^2/2}dt}
$$
### Welch's t-test
For smaller sample sizes, Welch's t-test is preferred because it produces lower false positive rates in cases of unequal sizes and variances. In Pulse, Statsig automatically applies Welch's t-test when the degrees of freedom $\nu \< 100$.
Statsig computes the t-statistic (also known as t-score) identically to the two-sample z-statistic above. Statsig computes the degrees of freedom $\nu$ using:
$$
\nu = \frac\{\left(var(\overline X\_t) + var(\overline X\_c)\right)^2}\{\frac\{var(\overline X\_t)^2}\{N\_t - 1}+\frac\{var(\overline X\_c)^2}\{N\_c - 1}}\
:= \frac\{var(\Delta\overline\{X})^2}\{\frac\{var(\overline X\_t)^2}\{N\_t - 1}+\frac\{var(\overline X\_c)^2}\{N\_c - 1}}
$$
Statsig then obtains the p-value from the t-distribution with $\nu$ degrees of freedom.
### One-sided z-test
The procedure for a one-sided z-test computes the z-statistic $Z$ in the same way as the two-sided test above.
The one-sided p-value comes from the standard normal cumulative distribution function, but with the following differences:
$$
p-value =
\begin\{cases} 1 - \frac\{1}\{\sqrt\{2\pi}} \int \limits \_\{-\infty}^\{Z}\{e^\{-t^2/2}dt} &\text\{if right-hand test}\\\\
\frac\{1}\{\sqrt\{2\pi}} \int \limits \_\{-\infty}^\{Z}\{e^\{-t^2/2}dt} &\text\{if left-hand test}
\end\{cases}
$$
where:
* $Z$ is computed as shown in the two-sided test above. This uses the signed z-statistic, not the absolute value used in the two-sided p-value.
================================================================================
# Metric Deltas
URL: https://docs.statsig.com/experiments/statistical-methods/metric-deltas
Updated: 2026-07-11
================================================================================
## Computing metric deltas
A metric delta refers to the difference in metric values between two groups, by default the test and control groups. This difference represents the impact measured in experiment results. To account for the different number of users (or units) in each group, Statsig compares the mean metric value per user, not the total.
{% callout type="note" %}
**Selecting Groups**
All deltas are defined as the difference between a "treatment" group compared to a presumably unchanged "control" group. Statsig also enables you to compare any two groups.
{% /callout %}
Two different metric deltas are available in Pulse. The **absolute delta** is the difference between the two means:
$$
\Delta \overline\{X}=\overline\{X}\_t-\overline\{X}\_c
$$
Understanding the impact relative to the baseline value of the metric is often useful. For example, an absolute delta of +1 clicks/user has different meanings with a baseline value of 1 (+100% increase) vs. a baseline value of 100 (+1% increase). Statsig computes the **relative delta** using the mean of the control group as the baseline:
$$
\Delta \overline\{X} \\%=\frac\{\overline\{X}\_t-\overline\{X}\_c}\{\overline\{X}\_c} \times 100 \\%
$$
{% callout type="note" %}
If you reverse the order of group comparison in Pulse to be "control" vs "treatment", then all deltas are reversed and the direction of change is inverted.
{% /callout %}
## Computing means
Computing group means correctly is critical for meaningful metric deltas. The methodology for calculating metric means depends on the metric type.
### Event count and sum metrics
These metrics represent totals: number of times an event occurs, sum of time spent, total purchase amount, and similar values. The mean is the average user-level total during the analysis period.
The mean value of the metric $X$ for a group is given by:
$$
\overline\{X}=\frac\{1}\{N} \sum\_\{i=0}^N \sum\_\{d=0}^\{n\_i} X\_\{i, d}
$$
where:
* $N$ is the number of users in the group
* $n\_i$ is the number of days during the analysis period that user $i$ was the experiment
* $X\_\{i,d}$ is the metric value for user $i$ on day $d$
{% callout type="note" %}
Statsig includes only user metrics recorded after a user has been exposed to the experiment in the group mean.
{% /callout %}
### User accounting and event user metrics (and legacy event DAU)
Event User metrics set to "Daily Participation Rate" capture the number of distinct users that have the event each day. In Pulse results, Statsig normalizes these values by the number of days the user is in the experiment. This represents the probability that a user is daily active for that event: the daily participation rate. The group mean is given by:
$$
\overline\{X}=\frac\{1}\{N} \sum\_\{i=0}^N \frac\{1}\{n\_i} \sum\_\{d=0}^\{n\_i} X\_\{i, d}
$$
where:
* $X\_\{i,d}$ takes value 0 or 1 depending on if user $i$ has the event on a given day $d$.
Statsig computes the following user accounting metrics in the same way: *DAU, WAU, MAU\_28day, L7, L14, L28*
For new user accounting (*new\_DAU, new\_WAU, new\_MAU\_28day*), Statsig counts users that are new xAU at some point during the analysis window. The group mean is given by:
$$
\overline\{X}=\frac\{1}\{N} \sum\_\{i=0}^N \max \left(X\_i\right)
$$
Where $\max(X\_i)$ is the maximum value of the new xAU metric for user $i$.
{% callout type="note" %}
**event\_dau** metrics are now in legacy support only and Statsig no longer creates them for new events. Existing event\_dau metrics continue to be available for any of your new experiments and Statsig continues to compute them daily. For all new events, create an event\_user metric to measure daily active users.
{% /callout %}
### Custom ratios, means, retention, and stickiness metrics
These are metrics such as click-through rate, average purchase value, and sessions per user. Statsig computes them by dividing a numerator value, $X$, by a denominator value, $Y$. The mean value of a ratio metric $R$ for an experiment group is given by:
$$
\overline\{R}=\frac\{\frac\{1}\{N} \sum\_\{i=0}^N \sum\_\{d=0}^\{n\_i} X\_\{i, d}}\{\frac\{1}\{N} \sum\_\{i=0}^N \sum\_\{d=0}^\{n\_i} Y\_\{i, d}}=\frac\{\overline\{X}}\{\overline\{Y}}
$$
Where $N$ is the number of users in the experiment group that participate in the metric, i.e. have a non-zero denominator value. $X\_\{i,d}$ and $Y\_\{i,d}$ are the $X$ and $Y$ values for user $i$ on day $d$.
Different approaches exist for dealing with ratio metrics in experiments. Statsig selected this implementation because it is statistically sound and interpretable, given that:
* $R$ is the ratio of two means of independent observations: a set of user-level $X$ values and a set of user-level $Y$ values. The central limit theorem can therefore be used to separately obtain the summary statistics of $X$ and $Y$.
* Statsig computes the group means in the same way as the topline metric value, making the means easier to interpret and relate to the topline metric.
### Event user one-time event
For custom **event\_user** metrics with "One-Time Event" selected, Statsig computes how many users have the event at any time after the user enters the experiment. Statsig doesn't normalize this result by the number of days a user is in the experiment. The group mean is given by:
$$
\overline\{X}=\frac\{1}\{N} \sum\_\{i=0}^N X\_\{i}
$$
where:
* $N$ is the number of users in the group
* $X\_\{i}$ takes value 0 or 1 depending on if user $i$ has the event at any point after entering the experiment
================================================================================
# Standard Error & Mean Variance
URL: https://docs.statsig.com/experiments/statistical-methods/variance
Updated: 2026-07-11
================================================================================
The standard error (also denoted "SE" or "std err") of the mean of each group is required to compute the confidence interval and p-value of a metric delta between those groups. You obtain the standard error of the mean by dividing the sample standard deviation of $X$ by the square root of the number of users in the group.
$$
\sigma\_\{\overline X} = \frac\{\sigma\_\{X}}\{\sqrt\{N}} = \sqrt\{\frac\{var(X)}\{N}} = \sqrt\{var(\overline\{X})}
$$
Standard deviation is the square root of the variance. Because variances are easier to manipulate algebraically, Statsig derives the variance for each metric type and then takes the square root to obtain the confidence intervals.
Pulse displays the standard error of the mean of each group alongside the units and mean of each group.
## Computing variance
The variance of the absolute metric delta is the sum of the variances of the test and control means:
$$
var(\Delta \overline X) =var(\overline X\_t - \overline X\_c) = var(\overline X\_t) + var(\overline X\_c)
$$
The calculation reduces to correctly computing the variance of the means for each group.
### Count and sum metrics
For count and sum metrics, Statsig derives the variance of the sample mean for a given group directly from the sample variance:
$$
var(\overline\{X}) = \frac\{var(X)}\{N} = \frac\{\frac\{1}\{N-1}\sum\_\{i=0}^\{N}(X\_i-\overline\{X})^2}\{N}
$$
Where:
* $N$ is the number of users in the group
* $X\_i$ is the metric value for user $i$
* $\overline\{X}$ is the user-level average of $X$ for users in that group
### Ratio and mean metrics
Ratio and mean metrics combine multiple variables $X$ and $Y$ rather than a single variable $X$. The variance of these metrics depends on both the numerator and denominator variables, which are typically correlated. The metric of interest $R$ has a group mean $\overline\{R}$ and a group variance of the mean $var(\overline\{R}$).
For example, consider a *clicks per session* metric. The number of clicks and the number of sessions are two sets of observations from the same group of users, so they aren't independent of each other.
To account for this correlation, Statsig obtains the variance of the mean of a ratio metric $R$ using the delta method:
$$
var(\overline R) = var\left(\frac\{\overline X}\{\overline Y}\right)
:= \left(\frac\{\overline X}\{\overline Y}\right)^2 \cdot \left(\frac\{var(\overline X)}\{\overline X^2} + \frac\{var(\overline Y)}\{\overline Y^2} - 2 \cdot \frac\{covar(\overline X, \overline Y)}\{\overline X\cdot \overline Y} \right)
$$
where Statsig computes the variance of the numerator and denominator means in the same way as for count metrics above, and the covariance is
$$
covar(\overline X, \overline Y) = \frac\{covar(X, Y)}\{N} = \frac\{\frac\{1}\{N-1}\sum\_\{i=0}^\{N}(X\_i-\overline X)\cdot (Y\_i-\overline Y)}\{N}
$$
================================================================================
# Delta Method
URL: https://docs.statsig.com/experiments/statistical-methods/methodologies/delta-method
Updated: 2026-07-11
================================================================================
## Delta method for ratio metrics
Statsig uses the delta method when calculating the variance for variables that have a numerator and denominator.
The variance of ratio and mean metrics depends on the numerator and denominator variables, which are typically correlated. For example, consider a *clicks per session* metric. The number of clicks and the number of sessions are two sets of observations from the same group of users, so they aren't independent of each other. To account for this correlation, Statsig obtains the variance of a ratio metric *R* using the delta method:
{% figure %}

{% /figure %}
where the variance of the numerator and denominator means are computed as described for count metrics, and the covariance is
{% figure %}

{% /figure %}
## Delta method for relative lifts
Statsig may also use the delta method when calculating the confidence interval for relative lifts. The other methodology is [Fieller Intervals](/experiments/statistical-methods/methodologies/fieller-intervals). The delta method is a heuristic for Fieller Intervals that converges with a large population.
================================================================================
# Bot Traffic
URL: https://docs.statsig.com/experiments/monitoring/bots
Updated: 2026-07-11
================================================================================
## Bots and filtering
A common source of frustration when monitoring gate traffic is online bot traffic from sources like search engines and AI scrapers, which makes it harder to measure how many real users are seeing your changes. Statsig has bot filtering in place to remove known bots from your exposures data, so the exposure counts and analytics you see reflect real users only.
Bot filtering applies to all types of exposures data, not just feature flags. Analysis results for feature flags, holdouts, layers, and experiments all have bots filtered out. For more on bot filtering, refer to the [Statsig Blog](https://www.statsig.com/blog/guide-online-bot-filtering).
After Statsig filters bot data from your exposures data, it isn't viewable in the Statsig console. Contact Slack support if you have additional questions.
### Controlling gates and experiments for bots
By design, Statsig doesn't block bots from receiving your feature flags and experiments. Statsig filters their exposures from analysis data and the exposure count you see in Pulse. The API and SDK results for bots are unchanged; bots are served configs and variants following your setup.
You may want to restrict what features bots see. For example, if you are testing a new homepage variant and don't want search engines to index it yet, you can do so through Segments:
1. Create a "Known Bots" Segment for your project:
Create a new segment, ensuring that it will be a **conditional** segment.
{% figure %}

{% /figure %}
After creating the segment, add a new rule. Set Criteria to "Browser Name". Leave Operator as "Any Of". In the Values field, paste the following string in its entirety. (There is a copy button to the right.) When pasting, the Statsig console splits the list into individual bot names.
```
Scomplerbot, WincherBot, fixbot, keys-so-bot, MojeekBot, Gulper Web Bot, Mattermost-Bot, SerendeputyBot, uipbot, WebCrawler, HearsayPDFBot, WRTNBot, BublupBot, InsytfulBot, DingTalkBot, uk_ldfc_renderbot, crawlers, ImagesiftBot, idealo-bot, taboolabot, KlaxoonBot, SemrushBot, archiver/3.1.1 +http://www.archive.org/details/archive.org_bot, StractBot, crawler_eb_germany_2, exabot, DocBase Crawler, co Bot, Superfeedr bot, Pokey_Bot, GooglePlusBot, OtherwebBot, PubMatic Crawler Bot, SiteAuditBot, Gensparkbot, wpbot, archive.org_bot, Audisto Crawler, amazon-product-discovery-bot, Atomseobot, Googlebot-Mobile, hubspot crawler, XoviOnpageCrawler, PerplexityBot, QualifiedBot, YodaoBot, BitSightBot, GG PeekBot, SMTBot, amazonproductbot, FAST-WebCrawler, TwitterCommerceBot, WellKnownBot, PAGEFREEZER CRAWLER, dbot, htc_botdugls, RavenCrawler, oBot, notebot, ViberBot, KStandBot, scoopit-crawler, SpeechifyBot, Spider_Bot, txt Crawler, net/bot, BugBountyBot, Letianpai_Robot, by fynd.bot, discobot, LineBotWebhook, ahrefsbot, Veoozbot, tkbot, coccocbot, Googlebot-Video, Streamline3Bot, Zoombot, adbeat_bot, msnbot, Nextdoorbot, node DuckAssistBot, redditbot, Xing Bot, DocSearch Crawler, ; bot, Storebot, playwright-bot, 47_safeAreaBottom, online-webceo-bot, SmarshBot, BeeperBot, ChannelBot, BrightEdge Crawler, //boteden, Quantcastbot, SpringserveBot, IAS Crawler, managr-webcrawler, ) Bot, Dragonbot, crawler4j, tyseobotmobile, IVW-Crawler, SEBot, sap-search-web-crawler, AndersPinkBot, Dcard-link-preview-bot, Mediumbot, Light Crawler, dataforseobot, Better Uptime Bot, CCBot, es_bot, DuckAssistBot, SeznamBot, telegrambot, crawler, Jugendschutzprogramm-Crawler, SeoCherryBot, GroupMeBot, HyperMegaBotGettingOnlyHTMLsFromYourWebsite, our-crawler, Slackbot-LinkExpanding, YandexMobileBot, Web-Crawler, PaperLiBot, Swiftbot, Paqlebot, YandexRenderResourcesBot, MetaJobBot, SynologyChatBot, GenomeCrawlerd, robot, StatusCakeBot, node bitlybot, Your robot, Pharosbot, TSMbot, WalluBot, slackbot, AmazonAdBot, AspiegelBot, EzoicBot, TiggeritoBot, eventseekerBot, AwarioBot, Leikibot, Timpibot, like Gecko) bot, Quora-Bot, JobBot, googlebot, PetalBot, eu bot, LinkArchiver twitter bot, DF Bot, Screaming Frog Wise SEO Spider, Clickagy Intelligence Bot, BLP_bbot, bitlybot, WazzupCrawler, web-crawler, pingbot, yoozBot, triptease-bot, Plesk screenshot bot, Magus Bot, node Screaming Frog SEO Spider, YextBot, seobilitybot, tyseobot, applebot, bingbot, GetLocalBot, TwitterBot, rogerbot, Preview Service; bot, traq-ogp-fetcher-curl-bot, seesawbot, Greppr Web Crawler, ResearchBot, web-bot, iAskBot, JobboerseBot, CriteoBot, FandomOpenGraphBot, com feedbot, Amazon-Advertising-ad-standards-bot, MotoMinerBot, peer39_crawler, Discordbot, DuckDuckGo-Favicons-Bot, KeybaseBot, adsbot, Open Graph Bot, emulate-seobots, bountybot, InfobipCrawler, GoogleBot, macox bot, Google-bot, captify-crawler, Robot, aiHitBot, fedistatsCrawler, ExtendedStayBot, NetpeakCheckerBot, com/bots, Automattic Analytics Crawler, Blog Rssbot, Dubbotbot, Rightlander Crawler, ClarityBot, Cookiebot, UOrgTestingBot, AcademicBotRTU, SEMrushBot, Server Crawler, Diffbot, DiscourseBot, chatbot, VirusTotalBot, SaberBot, TZUnfurlBot, Mail.RU_Bot, Monsidobot, YandexAccessibilityBot, preview service; bot, ecoresearchCrawler, PulsePoint-Crawler, DataForSeoBot, petalbot, Xbot, LinkedInBot, GnowitNewsbot, vebidoobot, Bawaab_bot, Brightbot, ClineCrawler, ; Bot, MSIECrawler, MoodleBot, Testcrawler, AASA-Bot, GPTBot, StrapBot, 5) bot, mj12bot, screaming frog seo spider, HubSpot Crawler, COIBotParser, OcelotBot, com crawler, Pinterestbot, VelenPublicWebCrawler, Firefox superpagesbot2, Parser Robot, GrapeshotCrawler, Mediatoolkitbot, am a bot, semrushbot, SearchAtlas Bot, DiffeoBot, IBM-Crawler, spbot, DatoCmsSearchBot, SISTRIX Crawler, bountybotttt, Summalybot, ID bot, node AppleNewsBot, DotBot, TesseractBotAgent, foundeebot, BadooBot, BacklinksExtendedBot, wowLink Crawler, about-crawlers, find-seo-bot, GraphiteBot, Sidetrade indexer bot, BLEXBot, rc-crawler, FacebookBot, nerdybot, Senutobot, Facebot, //botim, WallabyupBot, TurnitinBot, XBot_Senior, node ZoominfoBot, AhrefsBot, Exabot, PhaverBot, Applebot, TermlyBot, SemjiBot, Space Unfurl Bot, Slackbot, bidswitchbot, //botsin, AdsBot-Google, Morningscore Bot, DuckDuckBot, UptimeRobot, ClaudeBot, naverbookmarkcrawler, PingdomBot, Web Crawler, PlurkBot, node GrowSEOBot, WebExplorerSearchBot, node FullStoryBot, WebwikiBot, bot, policy adbeat_bot, trendictionbot0, ezoicbot, Catrobatbot, AdsTxtCrawlerTP, com/bot, Nigooutbot, PiBot, pinterestbot, http-spiders-bot, Ocarinabot, msnbot-media, AppsFlyerBot, SeobilityBot, Impressumscrawler, SurdotlyBot, cXensebot, Amazonbot, Rankabot, 2ip bot, harsilbot, FullStoryBot, com bot, Rhobot, FreshpingBot, twitterbot, Twitterbot, Caliperbot, Googlebot-Image, osapon ) bot, yandexbot, MJ12bot, Taboolabot, ActiveComplyBot, MixrankBot, 48_safeAreaBottom, compatible; botify, LoomlyBot, Googlebot, ev-crawler, pagefreezer crawler, AwarioSmartBot, iCjobs Stellenangebote, jbot, aixnew_aibot, SemanticScholarBot, Wire LinkPreview Bot, Elastic-Crawler, UCMore Crawler, x28-job-bot, ISSCyberRiskCrawler, AnytypeBot, clever tech bot, LivelapBot, Screaming Frog SEO Spider, RyteBot, SiteGuruCrawler, XBot, SuperBot, TypetalkBot, RepoLookoutBot, obot, TimeTreeBot, siteauditbot, iASD_SpiderBot, semaltbot, PopeTech-ScanBot, SummalyBot, aka-bot, YandexBot, HatenaBlog-bot, Googlebot-News, yacybot, SiteCheckerBotCrawler, node AwarioSmartBot, turbotime, net-Robot, reurl-bot, Google-Display-Ads-Bot, node CCBot, fr_bot, CapterraBot, seo-audit-check-bot, gptbot, Testomatobot, Snap-URL-Preview (bot, robots, ZumBot, hstspreload-bot, Fedicabot, serpstatbot, Synapse (bot, NetSeer crawler, uk_ldfc_bot, PartnerOptimizer-bot, Scrapbox Bot, domainsbot, NE Crawler, startmebot, VelaBot, SeekportBot, Radius Compliance Bot, AdkernelTopicCrawler, Linkbot, AppleNewsBot, Jones Searchbot, DropboxPreviewBot, ZoominfoBot, edansbot, Baiduspider-render, Python Requests, YisouSpider, OAI-SearchBot, AliyunSecBot, Baiduspider, Bytespider, TikTokSpider, Claude-SearchBot, Qwantbot, Thinkbot, ABEvalBot, HanaleiBot, OpenindexSpider, VamosBot, StartmeBot, Bingbot, parisbot, BetaLiveCrawlBot, MetaBot, CharSiuBot, PlagAwareBot, io/bot, ShowUpCrawler, ScraperBot, Monibot, ShellBot, IbouBot, MatchboxBot, CookieYesbot, Stripebot, Clearscopebot, quillbot, Sogou web spider, Checker Spider,
```
{% figure %}

{% /figure %}
This segment can then be reused for all your launches.
2. Apply the Segment to your Gates and Experiments:
For Gates, create a new rule that controls the bot experience.
{% figure %}

{% /figure %}
For experiments, create a Conditional Override that forces units in this segment to receive whatever version you want.
{% figure %}

{% /figure %}
### Opting out of bot filtering
Statsig configures bot filtering at the project level. Admins can opt out of filtering through their console settings.
{% figure %}

{% /figure %}
### Suggesting new bots to Statsig
If you have discovered bots that Statsig doesn't include in the default set, or you have internal bots your company manages that you want applied to all bot filtering, reach out in Slack.
================================================================================
# One-Sample Test
URL: https://docs.statsig.com/experiments/statistical-methods/methodologies/one-sample-test
Updated: 2026-07-11
================================================================================
## One-sample tests (fixed-value test)
A one-sample test compares a single sample of data against a known or hypothesized value to determine whether there is a statistically significant difference. Unlike A/B tests that compare two groups, one-sample tests evaluate whether a single group differs from a specific benchmark, target, or historical baseline.
## When to use one-sample tests
One-sample tests are useful for comparing a single group against a known value:
- **Single Group Events**: When only one group can trigger certain events (for example, feature usage or error types), compare against an expected baseline.
- **Algorithm Testing**: Test whether an algorithm performs better than random (for example, whether a success rate differs from 50%).
## Statistical considerations
One-sample tests provide a way to make statistical inferences about whether observed data differs significantly from a hypothesized value. The test helps determine whether any observed difference is due to random variation or represents a true change in the underlying process.
## Enable fixed-value baseline comparison
1. Go to the setup page of an experiment
{% figure %}
{% /figure %}
2. Click the metric name
{% figure %}
{% /figure %}
3. Select **Use Fixed Baseline as Control**
{% figure %}
{% /figure %}
================================================================================
# Fieller Intervals
URL: https://docs.statsig.com/experiments/statistical-methods/methodologies/fieller-intervals
Updated: 2026-07-11
================================================================================
## Fieller Intervals
You can use Fieller Intervals as the methodology for calculating confidence intervals for the relative change between test and control groups.
The Delta Method is an approximation for the variance of a ratio between two variables, which Statsig then uses to establish a confidence interval. Fieller Intervals are an exact solution for the confidence interval.
In most cases, Fieller Interval results are very similar to results from the Delta Method. Because Fieller Intervals are more accurate, Statsig recommends using this methodology.
## Calculation
### 1: Determine if a Fieller interval is well-defined
Before applying Fieller’s Theorem, verify that the denominator of the relative lift metric $\overline\{X\_C}$ is significantly distinct from 0.
Calculate the parameter $g$:
$$
g = \frac\{Z\_\{\alpha/2}^2 \cdot \mathrm\{var}(X\_C)}\{(n\_C-1) \cdot \overline\{X\_C}^2}
$$
Where:
$Z\_\{\alpha/2}$ is the critical value associated with the desired confidence level $\mathrm\{var}(X\_C)$ is the variance of the control group metric values $n\_C$ is the number of units in the control group $\overline\{X\_C}$ is the mean of the control group metric values
When $g$ \< 1, the control mean is significantly different from 0, and you can use Fieller intervals.
### 2A: Apply Fieller interval formula
Since the control and test group results are independent of each other, you can drop the covariance terms in Fieller's Theorem.
$$
CI(\\% \Delta \overline\{X} ) = \frac\{1}\{1-g} \left( \frac\{\overline\{X\_T}}\{\overline\{X\_C}} \pm \frac\{Z\_\{\alpha/2}}\{\overline\{X\_C}} \sqrt\{ \frac\{\overline\{X\_T}^2}\{\overline\{X\_C}^2} \cdot \frac\{\mathrm\{var}(X\_C)}\{n\_C-1} + (1-g)\frac\{\mathrm\{var}(X\_T)}\{n\_T-1} } \right) - 1
$$
### 2B: Edge case: control mean not statistically distinct from zero
In rare cases (less than 5% of observed metric comparisons on Statsig), g $\geq$ 1, which means the control group’s mean isn’t statistically distinguishable from 0.
When $\overline\{X\_C}$ isn't statistically different from zero, the denominator of the relative lift calculation is unstable. This means that the confidence interval for the percent difference between test and control is unbounded.
When the confidence interval is unbounded, Statsig surfaces the relative lift observed during the experiment.
$$
\\% \Delta \overline\{X} = \frac\{\overline\{X\_T}-\overline\{X\_C}}\{\overline\{X\_C}}
$$
## Enable Fieller intervals in Statsig
Configure the relative confidence interval methodology in your *Experimentation Settings* at the organization level. Changing this setting only affects experiments created after the change.
{% figure %}

{% /figure %}
In many cases, the results are effectively the same as using the [Delta Method](/experiments/statistical-methods/methodologies/delta-method), but Fieller Intervals are more reliable for experiments with small sample sizes or noisy denominators. Statsig strongly recommends using Fieller Intervals.
In the experiment scorecard, Fieller Intervals appear as shown:
{% figure %}

{% /figure %}
================================================================================
# Stratified Sampling
URL: https://docs.statsig.com/experiments/advanced-setup/stratified-sampling
Updated: 2026-07-11
================================================================================
## What is stratified sampling
Stratified sampling involves dividing the entire population into homogeneous groups called strata (plural for stratum). Random samples are then selected from each stratum. For example, if you had XS and XL customers and randomized them into two groups (Control and Test), you would want both Control and Test to be balanced across XS and XL customers. You can also stratify based on a metric such as Revenue/User.
With large numbers, randomization typically solves this balance problem. However, in B2B scenarios and other relatively low-volume or high-variance scenarios, stratified sampling ensures this balance. Statsig supports both automated and manual stratified sampling. In tests where a tail-end of power users drive a large portion of an overall metric value, stratified sampling meaningfully reduces false positive rates and makes results more consistent. In Statsig's simulations, this approach produced around a 50% decrease in the variance of reported results.
## Automated stratified sampling
### How it works
The Statsig SDKs use a *salt* to randomize or bucket experiment subjects ([learn more](/faq#how-does-bucketing-within-the-statsig-sdks-work)). When you enable stratified sampling, Statsig tries n different salts (100 by default) and evaluates how balanced your groups are. Statsig evaluates this balance based on either a metric you select or an attribute you provide describing your experiment subjects, then picks and saves the best salt. [Learn more](https://statsig.com/blog/introducing-stratified-sampling).
{% figure %}

{% /figure %}
The selection space for the salts is large enough that stratifying multiple experiments on the same metric will not result in overlap. In Statsig's simulations, the groups were as independent as expected, which matched the literature on this topic.
### Enabling stratified sampling
You can enable this on an experiment under Advanced Settings on the experiment setup page. There are two ways to stratify on Statsig.
If you choose a metric to stratify on, Statsig uses that metric to balance the groups.
{% figure %}

{% /figure %}
If you instead choose an attribute or a classification (for example, S, M, L, XL), Statsig uses that to balance the groups.
* On Statsig Cloud, you'll upload a CSV (in Early Access)
* On Statsig Warehouse Native, you'll use Entity Properties
{% figure %}

{% /figure %}
After you select the Stratify button, Statsig analyzes a set of salts and picks the best one.
{% figure %}

{% /figure %}
## FAQ and best practices
* **What population is used when balancing?**
* When evaluating salts, Statsig computes balance using pre-experiment data for the entire targeted population of the experiment’s unit type (for example, all `userID`s or all `customerID`s) over the selected lookback window. There is no filtering on exposure because the experiment has not started yet.
* **How are new units handled after stratification?**
* Units that were not present in the pre-experiment data are still assigned deterministically by the chosen salt, i.e., effectively at random with respect to the balancing metric. They do not influence the salt selection and may introduce some drift from the initial balance.
* **Should I use stratified sampling for every experiment?**
* Not necessarily. Stratified sampling is most useful when you expect imbalance due to heterogeneous units (for example, “whales”) or skewed metrics. The tradeoff is time and compute cost that scales with the number of units and adds steps before starting an experiment. If you do not expect meaningful imbalance, a standard random split is generally recommended.
* **Does salt evaluation assume 100% allocation? What about running at less than 100%?**
* Yes. All candidate salts are evaluated assuming 100% of the targeted population is allocated. If you run the experiment at an allocation below 100%, random sampling of that subset can reintroduce imbalance (for example, by chance, some high-impact units may fall disproportionately into one arm). For the period you care most about inference, prefer 100% allocation to preserve the intended balance. Lower allocations are best used briefly for safe rollouts rather than for the full experiment duration.
* **Across candidate salts, is it the same set of users being evaluated?**
* Yes. Candidate salts are assessed over the same targeted population; only the randomization induced by the salt changes.
* **How long does stratification take?**
* Duration depends on the number of units and the metric/source being queried. There is no fixed SLA; larger populations take longer.
## Manual assignment for stratified sampling
When setting up an experiment, you can configure overrides (for example, force user X or Segment A into Control, force user Y or Segment B into Test). Overrides are intended for testing; overridden users are excluded from experimental analysis in Pulse results. To use manual assignment for stratified sampling, select the *Include Overrides in Pulse* checkbox. This includes manually overridden users in each variant in all metric lift analyses. You can configure 100% of experiment participants into your test variants manually, or configure some subset of participants into variants manually and randomly assign the remainder.
{% callout type="note" %}
While you can add overrides for an ID type that is different than the ID type of the experiment, those ID evaluations will not be resolved to the id type of the experiment and will not contribute to pulse results.
{% /callout %}
When you use the Statsig SDK for assignment, it handles randomization. When you control assignment of users, you're responsible for making sure users are balanced across experiment groups.
{% figure %}

{% /figure %}
## Related resources
[Morgan and Rubin 2012](https://projecteuclid.org/journals/annals-of-statistics/volume-40/issue-2/Rerandomization-to-improve-covariate-balance-in-experiments/10.1214/12-AOS1008.full) walks through the history, the philosophy, and the proofs of re-randomization, especially how re-randomization reduces the randomization variance of the difference in means. It's worth noting that "Standard asymptotic-based analysis procedures that do not take the re-randomization into account will be statistically conservative" was called out in the paper. However, to maintain consistent and comparable results across different methods, Statsig stays conservative with the t-test.
[Lin & Ding 2019](https://arxiv.org/abs/1906.11291) is another interesting read for your reference.
================================================================================
# Frequentist Sequential Testing
URL: https://docs.statsig.com/experiments/advanced-setup/sequential-testing
Updated: 2026-07-11
================================================================================
## What's the problem with looking early in a "standard" A/B test
Traditional A/B testing best practices (t-tests, z-tests, etc.) require that the readout of experiment metrics occur only once, when the target sample size is reached (that is, when your design duration has been reached and you have the sample size you need). This approach is called a "Fixed Horizon Test": when designing an experiment, you set the number of units you want to observe and commit to analyzing results only after the dataset is complete.
Continuous experiment monitoring ("peeking") for the purpose of decision making results in inflated false positive rates (the peeking problem), which can be much higher than the expected rate at your desired significance level.
## How peeking increases decision error rates
Continuous monitoring leads to inflated false positives because any time you consider ending an experiment early, you risk making an incorrect conclusion. At the core of a standard hypothesis test, you decide whether to "accept the null" hypothesis or "reject the null" hypothesis and accept the alternative. Any time you look early and allow the possibility of an early decision, you are potentially rejecting the null hypothesis even when the null hypothesis is correct.
## Why early results can be misleading
Metric values and p-values always fluctuate to some extent due to noise during any experiment. Results can move into and out of statistical significance because of this noise, even when there is no real underlying effect. Noisy fluctuations result from random unit assignment and unpredictable user behavior, and can't be eliminated entirely. Noise levels vary by test, depending on what you are testing and who your users are. Tests also vary over time in the amount of noise they see, as adding more users and observing them longer tends to help random fluctuations even out.
Peeking introduces selection bias when it causes an experimenter to adjust the readout date. When an experimenter makes any early decision about results (for example, "is the result stat-sig; can we ship a variant early?") the chances increase that the decision is based on a temporary snapshot of results that are always fluctuating. The experimenter is potentially selecting a stat-sig result that wouldn't appear if the data were analyzed only once at the full, pre-determined completion of the experiment. In frequentist A/B test procedures, early decisions can only increase the false positive rate (declaring an experimental effect when there is none), even when the intention is to make a less-biased decision.
## How Sequential Testing works for an A/B test
In Statsig's implementation of Sequential Testing, Statsig automatically adjusts p-values and confidence intervals for each preliminary analysis window to compensate for the increased false positive rate associated with peeking, as shown in the Results tab:
{% figure %}

{% /figure %}
*In this example, the confidence intervals for each metric are expanded using the "wings" or "tabs". This is a quick visual indicator that sequential testing is enabled and shows how much the intervals have been expanded.*
{% figure %}

{% /figure %}
*In this example, the sequential testing adjustment determines whether the indicated result is declared stat-sig.*
The goal of Sequential Testing is to enable early decision making when observations are sufficiently strong to outweigh random fluctuations, while limiting the risk of false positives. Although peeking is typically discouraged, regular monitoring with sequential testing is valuable in some cases:
* Unexpected regressions: When experiments have bugs or unintended consequences that severely impact key metrics, sequential testing helps identify these regressions early and distinguishes significant effects from random fluctuations.
* Opportunity cost: When a significant loss may result from delaying the experiment decision (such as launching a new feature ahead of a major event or fixing a bug), sequential testing can support an early decision if key metrics show improvement. Use caution: an early stat-sig result for certain metrics doesn't guarantee sufficient power to detect regressions in other metrics. Limit this approach to cases where only a small number of metrics are relevant to the decision.
{% callout type="tip" %}
Sequential testing can be used anywhere you do an experimental analysis. This includes your main experimental Results page as well as any [custom queries](/experiments/interpreting-results/custom-queries).
{% /callout %}
## Quick guides
### Enable sequential testing results
In the **Setup** tab of your experiment, with Frequentist selected as your Analytics Type, enable Sequential Testing under the Analysis Settings section. You can toggle this setting at any time during the life of the experiment and don't need to enable it before the experiment starts.
{% figure %}

{% /figure %}
### Interpreting sequential testing results
Click **Edit** at the top of the metrics section in Pulse to toggle Sequential Testing on/off.
{% figure %}

{% /figure %}
When enabled, Statsig applies an adjustment to results calculated before the target completion date of the experiment.
{% figure %}

{% /figure %}
The dashed line represents the expanded confidence interval resulting from the adjustment. The solid bar is the standard confidence interval computed without any adjustments. If the adjusted confidence interval overlaps with zero, the metric delta isn't stat-sig at the moment, and the experiment should continue as planned.
Sequential testing is a reliable way to make an early decision, particularly for early detection of regressions. Early decision-making often results in underpowered lift estimates with a high degree of uncertainty. If making the correct decision is important, use statistically significant sequential testing results. If accurate measurement is important, wait for full power as estimated by your pre-experimental power calculation. Statsig doesn't calculate statistical power on post-hoc experimental results (refer to section "Post-hoc Power Calculations are Noisy and Misleading" in [Kohavi, Deng, and Vermeer, A/B Testing Intuition Busters](https://bit.ly/ABTestingIntuitionBusters)).
## How Statsig implements Sequential Testing
### Two-Sided Tests
#### Confidence Intervals
Statsig uses mSPRT based on the approach proposed by Zhao et al. in this [paper](https://arxiv.org/pdf/1905.10493.pdf). The two-sided Sequential Testing confidence interval with significance level $\alpha$ is given by:
$$
CI^\*(\Delta \overline\{X}) = \Delta \overline\{X} \pm Z^\*\_\{\alpha/2} \cdot \sqrt\{V}
$$
where
* $Z^\*\_\{\alpha/2}$ is the z-critical value, modified for sequential testing:
$$
Z^\*\_\{\alpha/2} = \sqrt\{\frac\{(V+\tau)}\{\tau}\left(-2\ln(\alpha/2)-\ln(\frac\{V}\{V+\tau})\right)}
$$
* $V$ is the standard variance of the delta of means when computing [variance](/experiments/statistical-methods/variance). It can be obtained from the sample variance of the test and control group means:
$$
V = var(\Delta \overline X)
\= var(\overline X\_t) + var(\overline X\_c)
\= \frac\{var(X\_t)}\{N\_t} + \frac\{var(X\_c)}\{N\_c}
$$
* $\tau$ is the mixing parameter given by:
$$
\tau
\=(Z\_\{\alpha/2})^2\cdot\frac\{var(X\_t)+var(X\_c)}\{N\_t+N\_c}
$$
* $Z\_\{\alpha/2}$ is the z-critical value used in the non-sequential test, for the desired significance level (1.96 for the standard $\alpha = 0.05$)
Statsig has validated that this parameter satisfies the expected False Positive Rate and provides enough power to detect large effects early. More details on this analysis are available [here](https://www.statsig.com/blog/sequential-testing-on-statsig).
#### p-Values
To produce p-values for sequential testing that are consistent with the expanded confidence intervals above, modify the [p-value methods](/experiments/statistical-methods/p-value).
The goal is to evaluate the mSPRT test so that the Type I error remains approximately equal to $\alpha$, and so that the sequential testing p-value is consistent with the expanded confidence interval. (I.e. A CI that includes 0.0% should have p-value ≥ $\alpha$, and one that excludes 0.0% should have p-value \< $\alpha$.)
The observed z-statistic (i.e. z-score) remains unchanged. Instead of evaluating $Z$ on a standard-normal distribution $N(0, 1)$, Statsig evaluates against another normal distribution $N(0, \sigma^2)$ with mean of zero and standard deviation $\sigma$. For a two-sided test, to limit the probability of an observed $Z$ exceeding $Z^\*\_\{\alpha/2}$ (assuming the null hypothesis to be true) to $\alpha$, you can find the unknown parameter by solving for $\sigma$:
$$
\sigma=\frac\{Z\_\{\alpha/2}^\*}\{\sqrt\{2} \cdot erf^\{-1}(1-\alpha)}
$$
where $erf^\{-1}$ is the [inverse error function](https://en.wikipedia.org/wiki/Error_function#Inverse_functions).
From here we can compute the two-sided sequential testing p-value as:
$$
\text\{p-value}^\* = 2 \cdot \frac\{1}\{\sqrt\{2\pi}} \int \limits \_\{-\infty}^\{-|Z|} \frac\{e^\{- \frac\{t^2}\{\{2\sigma^2}}}}\{\sigma}dt
$$
where $Z$ is the observed z-statistic (i.e. z-score) as usual.
### One-Sided Tests
Statsig modifies each step for one-sided sequential testing.
$$
CI^\*(\Delta \overline\{X}) = \begin\{cases}
\left\[\Delta \overline\{X} - Z^\*\_\{\alpha} \cdot \sqrt\{V}, \quad +\infty \right) & \text\{if right-sided test} \\\\
\\\\
\left(- \infty, \quad \Delta \overline\{X} + Z^\*\_\{\alpha} \cdot \sqrt\{V} :\right] & \text\{if left-sided test} \\\\
\end\{cases}
$$
$$
\text\{p-value}^\* = \begin\{cases}
1 - \frac\{1}\{\sqrt\{2\pi}} \int \limits \_\{-\infty}^\{Z} \frac\{e^\{- \frac\{t^2}\{\{2\sigma^2}}}}\{\sigma}dt \quad \text\{if right-sided test} \\\\
\\\\
\frac\{1}\{\sqrt\{2\pi}} \int \limits \_\{-\infty}^\{Z} \frac\{e^\{- \frac\{t^2}\{\{2\sigma^2}}}}\{\sigma}dt \quad \text\{if left-sided test} \\\\
\end\{cases}
$$
where
* $Z^\*\_\{\alpha}$ is the one-sided test z-critical value, modified for sequential testing:
$$
Z^\*\_\{\alpha} = \sqrt\{\frac\{(V+\tau)}\{\tau}\left(-2\ln(\alpha)-\ln(\frac\{V}\{V+\tau})\right)}
$$
* $V$ is the same as for two-sided tests.
* $\tau$ is the mixing parameter given by:
$$
\tau
\=(Z\_\{\alpha})^2\cdot\frac\{var(X\_t)+var(X\_c)}\{N\_t+N\_c}
$$
* $Z\_\{\alpha}$ is the one-sided z-critical value used in the non-sequential test, for the desired significance level (1.645 for the standard $\alpha = 0.05$)
* $\sigma$ is solved via:
$$
\sigma = \begin\{cases}
\frac\{Z\_\{\alpha}^\*}\{\sqrt\{2} \cdot erf^\{-1}(1 - 2 \alpha)} & \text\{if right-sided test} \\\\
\\\\
\frac\{- Z\_\{\alpha}^\*}\{\sqrt\{2} \cdot erf^\{-1}(2 \alpha - 1)} & \text\{if left-sided test}
\end\{cases}
$$
* $Z$ is the (signed) observed z-statistic as usual (i.e. z-score)
================================================================================
# Bayesian Experiments
URL: https://docs.statsig.com/experiments/advanced-setup/bayesian
Updated: 2026-07-11
================================================================================
## How Bayesian testing works in Statsig
Experiments are frequentist by default. To switch to Bayesian mode, go to Advanced Settings.
{% figure %}

{% /figure %}
You can't modify the experiment type after the experiment starts.
{% figure %}

{% /figure %}
Deep-dive analysis reflects Bayesian statistics.
{% figure %}

{% /figure %}
### Informed Bayesian
Bayesian experiments let you specify a prior belief on the relative average treatment effect. Statsig combines the prior distribution with the observed data to display prior-adjusted results. To enable this, select the "use informative priors" option.
{% figure %}

{% /figure %}
### Drawing the correct prior distribution from historical data
When using Bayesian with informative priors, your organization must understand what influence the priors have over your experimental results and must have established a reliable prior based on domain knowledge. Use these patterns to derive priors:
1. You can use the $AVG(\text\{average treatment effect})$ of past experiments with a similar setup and population as your prior mean. You can use the standard deviation, or a multiple of it, as the prior standard error.
2. You can also use the $AVG(\text\{observed standard error})$ as your prior standard error.
### Implementation details
Denote $\mathcal\{N}(ATE\_\{prior}, STE\_\{prior}^2)$ as the prior distribution, where $ATE\_\{prior}$ is the average treatment effect and $STE\_\{prior}$ is the standard error. Similarly, $\mathcal\{N}(ATE\_\{observed}, STE\_\{observed}^2)$ as the observed distribution.
The posterior distribution is then calculated as
$$
ATE\_\{post} =
\frac\{
\frac\{ATE\_\{prior}}\{STE\_\{prior}^2} +
\frac\{ATE\_\{observed}}\{STE\_\{observed}^2}
}\{
\frac\{1}\{STE\_\{prior}^2} +
\frac\{1}\{STE\_\{observed}^2}
}
$$
$$
STE\_\{post}^2 =
\frac\{1}\{
\frac\{1}\{STE\_\{prior}^2} +
\frac\{1}\{STE\_\{observed}^2}
}
$$
If the prior is not specified, the $\mathcal\{N}(ATE\_\{prior}, STE\_\{prior}^2)$ is represented as $\mathcal\{N}(0, \infty)$.
### Bayesian statistics glossary
Bayesian A/B tests use different terminology from the frequentist framework. These terms are often more intuitive for communicating results to non-technical audiences.
| Term | Definition |
|---|---|
| Credible Interval | The interval that contains the true parameter at the given probability |
| Chance to Beat | The probability that the test is better than control |
| Expected Loss | The average potential risk if you ship the test variant |
================================================================================
# Sequential Probability Ratio Tests
URL: https://docs.statsig.com/experiments/advanced-setup/sprt
Updated: 2026-07-11
================================================================================
## What is SPRT?
The **Sequential Probability Ratio Test** (SPRT) is an advanced methodology for running AB tests that differs from the traditional Null Hypothesis Significance Test (commonly called [Frequentist](/experiments/statistical-methods/p-value) analysis). SPRT can meaningfully improve time to decision for your experiments, including detecting unwanted metric regressions much faster. SPRT results are also easier to share with stakeholders who are not familiar with P-values and significance levels. SPRT has no penalties for peeking; there is no need for sequential testing plans, alpha spending, or CI-penalties because SPRT is built as a sequential test methodology from the start.
{% figure %}

{% /figure %}
### Concepts
SPRT introduces a few key concepts that differ from standard Frequentist tests. At its core, SPRT relies on the **Likelihood Ratio (LR)** and Upper and Lower decision boundaries, **A** and **B**.
The Likelihood Ratio estimates the relative difference in the likelihood of two outcomes:
- **Numerator**: What you observe is due to an alternative hypothesis (you set) being correct.
- **Denominator**: What you observe is due to the null hypothesis being correct.
The Upper and Lower decision boundaries are determined by your joint tolerances for Type I and Type II errors.
- **A**: If LR exceeds this upper threshold, accept the Alternative Hypothesis.
- **B**: If LR is less than this lower threshold, accept the Null Hypothesis.
- When LR falls into the range between these thresholds, no decision can be made and you should continue collecting data.
{% figure %}

{% /figure %}
An LR of 5.8, for example, indicates that what you observed is 5.8x more likely under the alternative hypothesis than under the null hypothesis.
The Likelihood Ratio aligns with how most people think about comparing options. Rather than reporting P-values and significance levels, you can report a result such as "_With an LR of 3.5, it's 3.5x more likely that the feature worked._"
## Why SPRT?
- **Faster decisions:** SPRT allows you to reach conclusions more quickly, potentially reducing experiment run time.
- **Intuitive results:** Instead of p-values, SPRT uses the Likelihood Ratio, a more intuitive measure of evidence for or against your hypotheses.
- **Sequential analysis:** Statsig continuously evaluates data as it's collected, allowing for early stopping when sufficient evidence is reached. There's no penalty for “peeking” in SPRT experiments.
- **Clear outcomes:** SPRT enables you to confidently accept either the Null or Alternative hypothesis, rather than just “rejecting the null.”
- **Data-informed:** Statsig’s implementation uses your past data and power analysis to inform the likelihood calculations and decision thresholds.
## Comparing SPRT to other analysis methods
| Category | Frequentist | Bayesian | SPRT |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Test Statistic | P-value: _Probability of observing the results that are as extreme as the sample data if the null hypothesis is true_ | Posterior Probability: _Probability of Test better than Control given the observed data and your prior information_ | Likelihood Ratio: _Comparing the goodness of fit of two competing statistical models_ |
| Decision Threshold | Alpha (Industry standard 5%) | Posterior Probability and Credible Intervals | Upper & Lower Decision Boundary decided based one the alpha and beta you picked |
| Decision Framework | Reject/Fail to Reject the Null based on if p-value > 5% | Whether chance to beat control exceeds the pre-set decision threshold | Accept the Null Hypothesis, Accept Alternative Hypothesis, Or Continue based on the comparison of calculated likelihood ration with Upper/Lower Decision boundary |
| Allows Peeking | Yes, but with Sequential Testing Penalties | Yes, Unlimited | Yes, Unlimited |
| Requires Pre-Setup | Yes, requires sample size calculation based on historical metric mean and MDE | Optional, but you can define prior distribution per metric if you have previous knowledge which can accelerates the experiment or correct surprising results | Yes, requires historical information about each metric as well as MDE |
| Allows 1- and 2-Sided tests | Yes, per metric | Yes, per metric | Yes, per metric |
## How to use SPRT in Statsig
**Enabling SPRT:** Select SPRT as your analysis method when setting up an A/B test in the Statsig console.
{% figure %}

{% /figure %}
**Interpreting Results:** The experiment Results tab shows the latest likelihood ratio for each metric and indicates when a decision boundary has been reached, letting you accept the null or alternative hypothesis with confidence.
{% figure %}

{% /figure %}
## Computing SPRT Results
Statsig uses an updated version of Hajnal's two-sample t test ([Schnuerch and Erdfelder](https://martinschnuerch.com/wp-content/uploads/2020/08/Schnuerch_Erdfelder_2020.pdf), as modified by Derek Ho of Atlassian) in its SPRT calculations. The traditional ratio test using t- or F-distributions (Schnuerch and Erdfelder equations 8 & 10) can be shown to simplify to a ratio of standard-normal distributions.
$$
\begin\{split}
\{LR}
&= \frac
\{f(t^2| \{df}, \Delta)}
\{f(t^2| \{df}, 1)} \\\\
&= \frac
\{\phi(|z\_\{m}|; \theta, 1)}
\{\phi(|z\_\{m}|; 0, 1)} \\\\
\end\{split}
$$
where:
- $\phi(x; \theta, 1)$ is the PDF of a normal distribution of shape $\mathcal\{N}(\theta, 1)$ evaluated at $x$
- $z$ is the observed Z-statistic between the groups
$$
z = \frac
\{\Delta \bar\{X}}
\{\sigma\_\{\Delta\bar\{X}}}
\= \frac
\{\bar\{X}\_B - \bar\{X}\_A}
\{\sigma\_\{\Delta\bar\{X}}}
$$
$$
\sigma\_\{\Delta\bar\{X}}=\sqrt\{\frac\{var(X\_A)}\{N\_A}+\frac\{var(X\_B)}\{N\_B}}
$$
- $\theta$ is derived from **Cohen's d** set prior to the experiment for the particular metric being considered
$$
\theta = \frac
\{\delta}
\{\sqrt\{
\frac\{1}\{N\_A} + \frac\{1}\{N\_B}
}}
$$
- $N\_A$ and $N\_B$ are the number of observed units for each group
Since log likelihood ratios are a more convenient scale for reporting, taking the natural log of the LR value above simplifies the equation further:
$$
\begin\{split}
LLR
&= ln(LR) \\\\
&= |z\_m \cdot \phi\_m | - \frac\{1}\{2}\{|\phi\_m|}^2
\end\{split}
$$
### Power analysis and setting Cohen's d
SPRT requires that a value of [**Cohen's d**](https://en.wikiversity.org/wiki/Cohen%27s_d) be set prior to the start of the experiment for each metric being evaluated. Setting the parameter requires three components:
- **MDE**: A Minimum Detectable Effect to be measured, in units of percent
- **Mean**: A baseline average value for the metric, $\overline\{X}$
- **Standard Deviation**: A baseline standard deviation for the metric, $\sigma\_\{X}$
With these values, you can compute Cohen's d parameter for each metric:
$$
\delta = \frac\{\text\{MDE\\%} \cdot \overline\{X}}\{100 \cdot \sigma\_\{X}}
$$
You can automate this process using Statsig's built-in query tooling. If you have a past experiment that ran on a similar set of units expected in the upcoming experiment, configure that experiment as a **Baseline Experiment** and a query automatically pulls the relevant metric parameters. You can also input all three parameters manually.
### Estimating the decision sample size
Cohen's d is used to compute experimental results after the experiment starts, but it can also estimate experiment duration in advance. Because SPRT allows you to look at results as often as desired, this is not the same as a "required sample size" in traditional frequentist testing. The **Decision Sample Size** is an estimate of the number of samples sufficient for an SPRT result for a metric to exceed either threshold and accept one of the hypotheses.
Given:
$$
A=ln\left(\frac\{1-\beta}\{\alpha}\right)
$$
$$
k=\frac\{n\_\{ec}}\{n\_\{et}}=\frac\{\text\{units expected in control}}\{\text\{units expected in treatment}}=\frac\{\text\{\\% units expected in control}}\{\text\{\\% units expected in treatment}}
$$
$$
n\_\{et} = \frac\{A}\{\frac\{1}\{2}\left(\frac\{k}\{1+k}\right)\delta^2}
$$
Then, the total number of expected units at decision time is:
$$
n\_e=n\_\{et}+n\_\{ec}=n\_\{et}(1+k)
$$
## Related resources
- [Original SPRT Paper (Wald, 1945)](https://projecteuclid.org/journals/annals-of-mathematical-statistics/volume-16/issue-2/Sequential-Tests-of-Statistical-Hypotheses/10.1214/aoms/1177731118.full)
- [The Sequential Probability Ratio t Test (Schnuerch & Erdfelder, 2020)](https://martinschnuerch.com/wp-content/uploads/2020/08/Schnuerch_Erdfelder_2020.pdf)
- [A two-sample sequential t-test (Hajnal, 1961)](https://www.jstor.org/stable/2333131)
## FAQ
**Can I use SPRT for all experiments?**\
SPRT is best suited for experiments where you want faster, sequential decisions and are comfortable with likelihood-based inference. For some experiment types, traditional methods may still be preferable.
**How does SPRT affect experiment duration?**\
SPRT can reduce experiment duration, especially when there is strong evidence for or against an effect. However, if the effect is small or data is noisy, the test may run longer.
**What are the limitations?**\
SPRT requires careful setup of thresholds and assumptions. It isn't a drop-in replacement for all frequentist methods, and may not be suitable for all experiment types.
**Is SPRT the same as Sequential Testing?**
SPRT is different from the Sequential Testing option. [Sequential Testing](/experiments/advanced-setup/sequential-testing) adjusts your Frequentist analysis method to allow repeated looks (i.e. "peeking"). SPRT is a completely separate experimental procedure and decision framework. They both allow for continuous "sequential" looking at experiment results, but otherwise they are separate methods for designing and running an A/B test.
================================================================================
# Ending an Experiment
URL: https://docs.statsig.com/experiments/ending/ending-experiment
Updated: 2026-07-11
================================================================================
When an experiment ends, one of two situations typically applies:
* Your experiment has statistically significant results and it’s time to ship an experience (control or test) to all your users.
* Your experiment has a problem and you need to stop it.
## Making a decision
To ship any of the groups in your experiment, including control, to all your users, [Make a Decision](/experiments/ending/make-decision) on the experiment. This ships the parameters of the selected variant to all users, and if the experiment is in a layer, Statsig updates the layer defaults to reflect your shipped experience.
## Stopping an experiment
If your experiment has a problem and you need to stop it, there are two options.
* If the problem means you no longer want to run this test, [Abandon the experiment](/experiments/ending/abandon). This puts the experiment in a finished state and all users fall back to defaults in code. You can still Reset it later if you decide to revisit the experiment.
* If one of the groups has a bug you plan to fix and you want to restart the experiment later, Reset the experiment. Alternatively, you can [disable a group](/experiments/implementation/disable-group) with the bug if you want to keep the other test groups running.
Resetting puts the experiment into an unstarted state. Every user receives the default experience (either the value defined in code or the layer default). The "salt" used to randomize a user’s group also changes. When you start the experiment again, Statsig randomly assigns users to a group that isn’t necessarily the same group they were in before the reset. This ensures that a group that didn’t perform well due to a bug or bad experience in the previous run isn’t negatively affected after you address the issue.
## Archiving an experiment
After you make a decision on an experiment, or when you abandon or reset it, you can archive the experiment. Archiving preserves the experiment’s history and results and makes the experiment read-only.
If you later decide to make the experiment active again, unarchiving the experiment makes it editable but doesn’t automatically restart it.
If the unarchived experiment was previously part of a layer and you intend to include it again, you must manually reset allocation from that layer.
================================================================================
# Make a Decision
URL: https://docs.statsig.com/experiments/ending/make-decision
Updated: 2026-07-11
================================================================================
Making a decision for an experiment ships the winning group to all your users.
After you make the decision, the variant that users see depends on whether you're using a **targeting gate** for your experiment. Experiment results remain accessible after you make a decision, but they stop updating. The last day of metrics is the day you make a decision on the experiment.
## Experiments with no targeting gate
When you ship a group in an experiment with no targeting gate, the parameter values from the shipped group become the default values for all your users going forward.
If the experiment uses parameters from a layer, the layer's parameters take on the shipped group's parameter values as their defaults. These are the values that all your users see going forward.
For example, suppose you have a **Demo Layer** configured with a parameter, **a_param**. Its default value is set to _layer_default_ as shown below.
{% figure %}
{% /figure %}
To run an experiment in **Demo Layer**, create **Demo Experiment** as shown below.
{% figure %}
{% /figure %}
You set up **Demo Experiment** with two groups: **Control** and **Test**, intending to experiment with new values for the layer-level parameter, **a_param** as shown below.
{% figure %}
{% /figure %}
Now if you decide to ship the **Control** group for the **Demo Experiment**, **a_param** takes the value set for the **Control** group as its default: _experiment_one_control_
{% figure %}
{% /figure %}
On the other hand, if you decide to ship the **Test** group, **a_param** takes the value set for the **Test** group as its default: _experiment_two_test_
{% figure %}
{% /figure %}
## Experiment with a targeting gate or targeting rules
When you ship a group in an experiment configured with a targeting gate or targeting rules, you can decide whether to continue targeting after shipping.
- If you _discontinue_ targeting, the parameter values from the shipped group become the default values for all your users going forward. If the experiment uses parameters from a layer, the layer's parameters take on the shipped group's parameter values as their defaults.
- If you decide to _continue_ targeting with a **targeting gate**, Statsig adds an override to the experiment layer so that:
- all users who **pass** the targeting gate see the shipped group's parameter values
- all users who **fail** the targeting gate see the default value (layer-level parameter defaults or the defaults you set for the parameter in your code)
- If you decide to _continue_ targeting with **targeting rules**, the experiment is only shipped to users who pass the inline targeting rules as set in the experiment setup.
{% callout type="info" %}
**Shipping with Targeting On**
If you decide to continue targeting, shipping a group doesn't update the default value of any layer parameters.
{% /callout %}
For example, suppose **Demo Experiment** is in **Demo Layer**, which has a parameter called **targeted_layer_param** with a default value of _targeted_layer_default_value_.
When you ship **Demo Experiment** and discontinue targeting,
**targeted_layer_param** takes on the value from the **Control** group, _targeted_layer_control_, as its default.
{% figure %}
{% /figure %}
If you continue targeting, **targeted_layer_param** acquires an override so that:
- all users who **pass** the targeting gate see the _overridden_ value of the parameter
- all users who **fail** the targeting gate see the _default_ value of the parameter
In this case, the default value of **targeted_layer_param** in **Demo Layer** doesn't change. Users who pass the targeting gate aren't eligible for future experiments run in this layer. Shipping experiments with targeting on is not recommended, especially when the experiment is in a layer.
{% figure %}
{% /figure %}
## Rolling out an experiment group
Rolling out an experiment group is an option when you have decided the winning variant but want to avoid a sudden, large shift of traffic into the winning variant group. Use automated rollouts to schedule gradual rollout phases, which increase your shipped group size to the target percentage by reallocating users from all other groups proportionally.
### Setting up rollouts
To set up rollouts, open the make decision form and select the winning group. From here you can use automated rollouts or ship with rollout.
{% figure %}

{% /figure %}
The ship with rollout option immediately updates the shipped group size. Manual rollouts clear any automated rollout phases.
{% figure %}

{% /figure %}
Alternatively, you can set up automated rollouts, which opens the following dialog to populate with rollout phases:
{% figure %}

{% /figure %}
Configure each phase of your Scheduled Rollout. To add phases to your rollout, select **Add Phase** and configure as many phases as needed.
Each scheduled rollout phase includes:
- Rollout date
- Rollout time\*
- Pass percentage
{% callout type="note" %}
Rollout times are available in 15 minute increments. Additionally, each configured phase represents a discrete increase to the next rollout percentage, not a gradual rollout amortized over the course of the entire phase.
{% /callout %}
After saving, you can preview the rollout and commit the schedule:
{% figure %}

{% /figure %}
### Resizing logic
During each phase, the rollout group is resized to the desired percentage, and all other groups are scaled proportionally in the following way:
You have _n_ groups with sizes:
G₁, G₂, ..., Gₙ
Their total sum is:
100 = G₁ + G₂ + ... + Gₙ.
Now, suppose you **set one group** (say the k-th group) to a **new size** Gₖ′. Let
Delta = Gₖ′ − Gₖ.
Because the **grand total** must remain 100, you need to **adjust the remaining groups** proportionally. Let
T = 100 - Gₖ
Then, for each group i ≠ k,
Gᵢ′ = Gᵢ − (Delta × Gᵢ / T).
In other words, each group other than k is decreased (or increased, if Delta \< 0) by its fraction of T.
### Notes
1. Experiment results are frozen to a snapshot of when the rollout decision is made.
2. Rolling out a group to 100% doesn't fully ship the experiment; configurations such as experiment/layer allocation, targeting, and overrides don't change. To fully ship the experiment, go through the usual flow without specifying any rollouts.
3. Groups can be rolled out and back, but the rollout percentage can't be lower than the group's original size.
{% callout type="info" %}
This is currently an Early Access feature.
{% /callout %}
## Shipping with a holdback
Shipping with holdback lets you release an experiment variant (the “shipped” group) to most of your users while keeping a percentage in the control group for ongoing comparisons.
1. Make a decision and select a group to ship: From the Make Decision dropdown, choose which variant you want to ship.
2. Turn on the Ship with holdback option and specify the percentage of users you want to keep in the control (holdback) group.
{% figure %}
{% /figure %}
3. Allocation of users:
The control (holdback) group is set to your specified percentage.
The remaining users are assigned to the shipped experience.
4. Splitting the shipped group:
The shipped group is divided into two segments: Test and Launched.
- The size of Test segment has the same percentage allocation as the holdback group for an [equal sized comparison](/experiments/holdouts-introduction#how-to-read-holdouts) of 50:50.
- The Launched segment no longer appears in the pulse results, but users in this segment continue to receive the shipped experience.
{% figure %}
{% /figure %}
5. Continue monitoring the pulse results that evaluate the Test segment vs. Control (holdback).
6. When you decide to end the holdback, you can make a decision to ship the variant to everyone, which includes users in holdback.
{% callout type="note" %}
Some users currently in control will move to the shipped group to achieve the desired allocation.
However, any user who has previously been in a test or shipped group won't be reassigned to control.
New users (those who have never been exposed to the experiment) are assigned based on the allocation percentages of each group.
{% /callout %}
The new pulse results (Test segment vs. Holdback) start when you ship with holdback. Your original experiment's results from the point of the holdback decision are retained and remain accessible.
From the experiment history, you can see the new log of the experiment decision and a **View Results Snapshot** button where you can view the read-only snapshot of original results.

Shipping with holdback maintains a dedicated, stable control group alongside a representative test segment of the shipped experience, making it easy to measure ongoing performance and user behavior after launch.
{% callout type="info" %}
This is currently an Early Access feature.
{% /callout %}
================================================================================
# Stop Assignments
URL: https://docs.statsig.com/experiments/ending/stop-assignments
Updated: 2026-07-11
================================================================================
## Stopping new user assignments in an experiment
After running an experiment for some time with users split across control and treatment groups, you may want to stop enrolling new users and analyze only the users who have been exposed so far. The Stop Assignment option lets you do this.
{% callout type="info" %}
You will need to configure [Persistent Assignment](/server/concepts/persistent_assignment) for this feature to work. If you don't configure this, people already exposed to the treatment groups will no longer get the treatment experience. Persistent Assignment is required even if you use stable identifiers.
{% /callout %}
## What Stop Assignment does
- Sets experiment allocation to 0%: no new users are enrolled in the experiment.
- Preserves existing user assignments: users already exposed continue to receive their control or treatment experience (only if Persistent Assignment is configured).
- New users aren’t checked against the experiment and instead get the project’s default experience. To ship a specific variant to all new users, make a decision on the experiment or set this in code.
## Enabling Stop Assignment option
You must first enable the **Stop Assignment** option in Project Settings for it to appear in the **Make Decision** modal. To enable this option, go to *Settings > Project Info* and toggle on the **Enable stop new user assignments for experiments** setting.
{% figure %}
{% /figure %}
## How Stop Assignment works
To stop assignment for an experiment, select the Make Decision dropdown as shown below.
{% figure %}

{% /figure %}
When you stop assignments for an experiment:
- After you stop assignment, the experiment stops enrolling new users. Statsig stops performing checks on new users.
- Previously exposed users continue receiving their consistent control or treatment experience. Configure [Persistent Assignment](/server/concepts/persistent_assignment) to preserve the user variants.
- Analysis continues as the experiment records new data points for already-exposed users.
Stop Assignment is an irreversible decision. After you make this decision, you cannot resume assigning users into your experiment.
================================================================================
# Conclude Experiment & Defer Decision
URL: https://docs.statsig.com/experiments/ending/conclude-experiment-defer-decision
Updated: 2026-07-11
================================================================================
## How Conclude Experiment and Defer Decision works
The "Conclude Experiment and Defer Decision" feature stops any further user allocation and data collection on an ongoing experiment. Use this feature when you want to decouple running an experiment from making a ship decision.
### Benefits
- **Focused decision making**: Decoupling the experiment from the decision gives you time to evaluate results, weigh options, and review findings with stakeholders across teams.
- **Flexibility in outcomes**: Decide whether to ship the control, implement the test, or abandon/restart the experiment based on comprehensive insights.
{% figure %}

{% /figure %}
### Enabling this option
You must first enable the "Conclude Experiment and Defer Decision" option in Project Settings for it to appear in the Make Decision modal. To enable this option, go to *Settings > Experimentation* in the **Product Configuration** section and toggle on the **Enable decision type - Conclude Experiment and Defer Decision** setting.
{% figure %}
{% /figure %}
### Key details
When you conclude an experiment:
- After you conclude the experiment, no new users are enrolled.
- All previously exposed users start receiving the default experience.
- Statsig stops further data collection on the experiment and concludes results.
- After concluding the experiment, you can still decide to ship control vs test variant, reset, or abandon the experiment.
### Reloads after experiment conclusion
You may notice slight changes in exposure and metric data if you trigger another reload after concluding an experiment. This doesn't mean new users are still being exposed. Depending on the timestamp of when the experiment was concluded versus when the latest exposure data landed in your warehouse in the previous reload, additional exposures from that time window may appear in the refreshed data.
### Summary
The "Conclude Experiment and Defer Decision" feature lets you make data-driven decisions without the risk of diluting results by continuously adding new participants or new data from existing participants. This structured approach ensures that all stakeholders are aligned before you make a decision.
================================================================================
# Abandon an Experiment
URL: https://docs.statsig.com/experiments/ending/abandon
Updated: 2026-07-11
================================================================================
When your experiment has an issue or you need to stop it for any reason, you can abandon it. Abandoning an experiment puts it into the unstarted state, which gives every user the default experience.
{% figure %}

{% /figure %}
When you abandon an experiment, the "salt" the experiment uses to randomize a user's group also changes. When you restart the experiment, Statsig randomly assigns users to a group that isn't necessarily the same group they were in before. This ensures that a group that didn't perform well due to a bug or bad experience in the previous run isn't negatively affected after you address the issue in the new run.
================================================================================
# Disable a Group
URL: https://docs.statsig.com/experiments/implementation/disable-group
Updated: 2026-07-11
================================================================================
## Disabling a group in an experiment
When you start an experiment with multiple test groups and find that one group is performing poorly or creating bad user experiences, you can disable that group while keeping the other test groups running.
### What to expect when a group is disabled
* Users already assigned to the disabled group begin to receive the default experience.
* Statsig can assign new users to the disabled group, and those users receive the default experience.
* Statsig continues tracking exposures for the disabled group.
* Sample Ratio Mismatch (SRM) isn't triggered from the disabled group.
* Statsig continues to calculate experiment results for the disabled group.
{% callout type="info" %}
Statsig continues showing the group and logging exposures so that you can verify that user experiences have recovered after the group was disabled.
{% /callout %}
### How to disable a group
* Log into the Statsig console at https://console.statsig.com
* On the left-hand navigation panel, select **Experiments**
* Select the experiment where you want to disable a group
* Click the **...** menu in the top right corner
* Select **Disable A Group**
* Select the experiment groups you want to disable, then select **Confirm**
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
## Reenable a group
To reenable a previously disabled group, use the **...** menu and select **Disable or Enable A Group**.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
{% callout type="info" %}
Users in any reenabled group may have experienced multiple treatment experiences over the course of the experiment. The metric data collected for the group may reflect this.
{% /callout %}
================================================================================
# How to Read Experiment Results (Formerly "Pulse")
URL: https://docs.statsig.com/experiments/interpreting-results/read-results
Updated: 2026-07-11
================================================================================
## Read Experiment Results
To read the results of your experiment, go to the **Results** tab. There you'll find your experiment hypothesis, **exposures**, and **Scorecard**.
### Exposures
{% figure %}

{% /figure %}
At the top of the Results page is the Exposures Chart. Exposures are the unique experimental units enrolled in the experiment: typically unique users, or devices for device-level experimentation. The timeline shows when the experiment started and how many exposures were enrolled each day. Use this chart to verify the rate at which users entered each group, the total number exposed, and whether the target ratio matches what you configured in experiment setup.
### Scorecard
The experiment **Scorecard** shows the metric lifts for all Primary and Secondary metrics you set up at experiment creation.
#### Immediately post-experiment start
For up to the first 24 hours after starting your experiment (before the daily metric results run), Statsig calculates the **Scorecard** section hourly. This applies only to Statsig Cloud; for WHN projects, you must reload results on demand or set up a daily schedule. This real-time scorecard lets you confirm that exposures and metrics are being calculated as expected and debug your experiment or gate setup if needed.
{% callout type="note" %}
Do **not** make any experiment decisions based on real-time results data in this first 24-hour window after experiment start. Call experiments only after the experiment has reached target duration, as set by your primary metric(s) reaching experimental power.
For more information about target duration, refer to [Target Duration](/experiments/create-new#target-duration).
{% /callout %}
Data in this early window is intended for diagnostics, not decision-making. The real-time view differs from daily results in the following ways:
- Metric lifts don't have confidence intervals
- No time-series view of metric trends
- No projected topline impact analysis
- No option to apply more advanced statistical tactics, such as CUPED or Sequential Testing
All of these are available in daily Results, which start showing in the next daily run.
#### Post-first day scorecard
{% figure %}

{% /figure %}
The daily results run calculates the difference between comparable randomization groups (for example, test and control) across your metric suite and applies a statistical test to the results. Refer to [Statsig's stats engine](/experiments/statistical-methods/introduction) for details.
For every metric, Statsig shows you:
- The calculated relative difference (Delta %)
- The confidence interval
- Whether the result is statistically significant
- Positive lifts are green
- Negative lifts are red
- Non-significant results are grey
The formula for calculating lift is:
Delta(%) = (Test - Control) / Control
Confidence intervals are reported at the selected significance level (95% by default). In a typical two-sided Z-test, we show the confidence interval as +/- 1.96 \* standard error.
Statsig automatically applies 99.9% winsorization to event_count, event_count_custom, and sum metrics. Winsorization caps extreme outlier values to reduce their impact on experiment results. For metrics added to the **Scorecard** or **Monitoring Metrics** sections of your experiment or gate, you can also apply optional statistical treatments such as CUPED (pre-experiment bias reduction) and sequential testing adapted confidence intervals. Refer to [the stats engine documentation](/experiments/statistical-methods/introduction) for more details.
- **Experiment results are computed for the first 90 days**: By default, Statsig computes experiment results for only the first 90 days of your experiment. Statsig notifies you by email as you approach the 90-day cap. At that point, you can extend the compute window by 30 days at a time. If the experiment runs beyond the compute window, Statsig stops adding new users to the experiment's results, but analysis for users already exposed continues until you make a decision on the experiment.
{% callout type="note" %}
This experiment result calculation window only affects whether a user is included in the experiment's analysis, and doesn't affect the treatment each user receives. New users still receive the experience for the group they get randomized into.
{% /callout %}
### Experiment results views
There are several views for Scorecard metric lifts:
- **Cumulative results (default view)**: Displays the aggregate difference between experiment groups and visualizes the corresponding confidence intervals.
- **Table view**: Displays the same data as the cumulative view but in a table format with additional fields.
- **Daily results**: Shows the difference between experiment groups aggregated based on days since start of experiment.
- **Days since exposure**: Shows the difference between experiment groups aggregated based on days since exposure to the experiment.
Cumulative results includes a detailed view on hover, where you can additionally view the raw statistics used in the metric lift calculations, as well as topline impact.
{% figure %}

{% /figure %}
### Dimensions
There are two ways to break down a given Scorecard metric: by a **User Dimension** or by an **Event Dimension**.
#### User dimensions
User Dimensions are user-level attributes that are either part of the user object you log, or additional metadata that Statsig extracts. Examples include operating system, country, and region.
You can create [custom "explore" queries](/experiments/interpreting-results/custom-queries) to filter or group by available user dimensions. For example, view results for users in the US, or for users on iOS grouped by country. Go to the **Explore** tab to draft a custom query.
{% figure %}

{% /figure %}
#### Event dimensions
Event Dimensions are the value or metadata logged as part of a custom event used to define the metric. To see results for a metric broken down by categories specific to that metric, [specify the dimension](/metrics/metric-dimensions) in the **value** or **metadata** attributes when you log the source event. For example, when you log a "click" event, you can also log the target category using the **value** attribute. Statsig automatically generates results for each category in addition to the top-level metric.
To see breakdowns for all categories within a metric, click on the (+) sign next to the metric.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
### Significance level settings
You can adjust these settings at any time to view Scorecard results at different significance levels.
- **Apply Benjamini-Hochberg Procedure per Variant**: Reduces the probability of false positives by adjusting the significance level for multiple comparisons. Refer to [Benjamini-Hochberg Procedure](/experiments/statistical-methods/methodologies/benjamini-hochberg-procedure) for details.
- **Confidence Interval**: Changes the confidence interval displayed with the metric deltas. Choose lower confidence intervals (for example, 80%) when tolerance for false positives is higher and fast iteration with directional results is preferred over longer experiments with greater certainty.
- **CUPED**: Toggle CUPED on or off using the inline settings above the metric lifts. This setting applies only to **Scorecard** metrics; Statsig doesn't apply CUPED to non-Scorecard metrics.
- **Sequential Testing**: Applies a correction to p-values and confidence intervals to reduce false positive rates when evaluating results before the target completion date, which mitigates the increased false positive rate caused by early result evaluation. Toggle Sequential Testing on or off using the inline settings above the metric lifts. This setting is available only for experiments with a set target duration.
{% figure %}

{% /figure %}
### Restarting results
{% figure %}
{% /figure %}
If your experiment has stopped computing results, you can resume updates by clicking the Restart button. Understand how Restart behaves before proceeding:
- A Restart isn't a [Reset](/experiments/ending/ending-experiment#stopping-an-experiment) of your experiment. A Restart doesn't re-salt (re-randomize) units in your experiment, and all users continue to receive the same group assignments.
- Statsig begins computing experiment results from the restart point, so your metric results start over. Old results may still be available in timeseries and explore query views, but Statsig doesn't carry them forward or update them.
- Your Cumulative Exposures chart updates based on new exposures, but the duration of the pause in computations affects whether the chart starts over from zero or your exposure count includes past exposures.
To avoid needing a Restart, extend experiments actively while they are running. Look out for email alerts from Statsig and check your experiments regularly.
================================================================================
# Slicing by User Properties
URL: https://docs.statsig.com/experiments/interpreting-results/userproperties
Updated: 2026-07-11
================================================================================
Statsig lets you slice results by user properties. Common examples include breaking down results by a user's home country, subscription status, or engagement level.
{% figure %}

{% /figure %}
For Statsig Cloud, Statsig captures and freezes user properties from the properties set on the user's first exposure. Statsig Warehouse Native also supports reading user properties from a warehouse table (Entity Properties). You can run custom queries on experiments to slice by user properties at any time.
## Pre-computed user properties
User properties that are frequently used to slice results can be pre-computed when using Statsig Warehouse Native. Configure these properties to be pre-computed on the experiment setup page, under the advanced settings. You can also configure team-level defaults or pre-configure them on an experiment template.
{% figure %}

{% /figure %}
After configuring pre-computed properties, you can also apply filters to all metrics on your results.
{% figure %}

{% /figure %}
================================================================================
# Pulse
URL: https://docs.statsig.com/experiments/interpreting-results/drill-down
Updated: 2026-07-11
================================================================================
## Metric tooltip
When you hover over a metric in Pulse, Statsig shows a tooltip with key statistics and deeper information.
{% figure %}

{% /figure %}
* **Group**: The name of the group of users. For Feature Gates, the "Pass" group is considered the test group while the "Fail" group is the control. In Experiments, these are the variant names.
* **Units**: The number of distinct units included in the metric. E.g.: Distinct users for user\_id experiments, devices for stable\_id experiments, etc.
* **Mean**: The average per-unit value of the metric for each group.
* **Total**: The total metric value across all units in the group, over the time period of the analysis.
### Calculation details
| Metric Type | Total Calculation | Mean | Units |
|------------------|-------------------------|----------------|----------------|
| event\_count | Sum of events (99.9% winsorization) | Average events per user (99.9% winsorization) | All users |
| event\_user | Sum of event DAU (distinct user-day pairs) | Average event\_dau value per user per day. Statsig calls this "Event Participation Rate" because it represents the probability a user is DAU for that event. | All users |
| ratio | Overall ratio: sum(numerator values)/sum(denominator values) | Overall ratio | Participating users |
| sum | Total sum of values (99.9% winsorization) | Average value per user (99.9% winsorization) | All users |
| mean | Overall mean value | Overall mean value | Participating users |
| user: dau | sum of daily active users | Average metric value per user per day. The probability that a user is DAU | All users |
| user: wau, mau\_28day | Not shown | Average metric value per user per day. The probability that a user is xAU | All users |
| user: new\_dau, new\_wau, new\_mau\_28day | Count of distinct users that are new xAU at some point in the experiment | Fraction of users that are new xAU | All users |
| user: retention metrics | Overall average retention rate | Overall average retention rate | Participating users |
| user: L7, L14, L28 | Not shown | Average L-ness value per user per day | All users |
### p-Value
In Null Hypothesis Significance Tests, the p-value is the probability that such an extreme difference arises by random chance when the experiment has no actual effect. A low p-value indicates the observed difference is unlikely to be due to random chance. In hypothesis testing, a p-value threshold determines which results are due to a real effect and which are plausibly due to random chance. ([p-value calculation](/experiments/statistical-methods/p-value))
### Reverse power
Reverse power is the smallest effect size that an experiment can reliably detect in its current state (some studies refer to this value as ex-post MDE). Statsig calculates it from the sample size and standard error from the control group. Importantly, reverse power does *not* depend on the observed effect size. In practice, reverse power answers such questions like: given how the test actually played out, what is the smallest effect we have sufficient power (typically 80%) to detect?
For a two-sided test, the reverse power for a given metric X is computed using the following equation:
$$
Reverse Power = \frac{(Z_{1-\beta} + Z_{1-\alpha/2})}{\overline{X}_{\text{control}}}\times \sqrt{\frac{\mathrm{var}(\Delta \overline{X})}{N_{\text{control}}}} \times 100\\%
$$
For a one-sided test, the reverse power for a given metric X is computed using the following equation:
$$
Reverse Power = \frac{(Z_{1-\beta} + Z_{1-\alpha})}{\overline{X}_{\text{control}}}\times \sqrt{\frac{\mathrm{var}(\Delta \overline{X})}{N_{\text{control}}}} \times 100\\%
$$
* $\overline{X}_{\text{control}}$ is the mean metric value across control users
* $var(Δ\overline{X})$ is the population variance of delta
* $N_{\text{control}}$ are the observed number of units in the control group
* $Z_{1-\beta}$ is the standard Z-score for the selected power. Typically ${1-\beta}$ = 0.8 and $Z_{1-\beta}$ = 0.84
* $Z_{1-\alpha/2}$ and $Z_{1-\alpha}$ are the standard Z-scores for the selected significance level in a two-sided test and in a one-sided test.
You can enable reverse power as an optional feature. To manage it, go to **Settings > Product Configuration > Experimentation > Organization** and toggle it on or off.
## Detailed view
Select **View Details** to access in-depth metric information. The detailed view contains three sections:
* **Time Series**: How the metrics evolve over time
* **Raw Data**: Group-level statistics
* **Impact**: How the experiment impacts the metric
### Time series
In this view, select and drag to zoom in on different time ranges. The drop-down offers three types of time series:
**Daily**: The metric impact on each calendar day without aggregating days together. This is useful for assessing day-over-day variability and the impact of specific events. This is the recommended time series view for Holdouts, because it highlights the impact over time as new features are launched.
{% figure %}

{% /figure %}
**Cumulative**: Shows the cumulative metric impact from the start of the experiment over time. This is useful for observing trends and seeing how your confidence interval changes over time.
{% figure %}

{% /figure %}
**Days Since Exposure**: Shows the metric impact based on how long a user has been in the experiment. Daily data for each user is aligned by the day the user entered the experiment (Day 0, Day 1, and so on), not by calendar date. This lets you distinguish early (novelty) effects from long-term effects. This view also shows pre-experiment data, which identifies biases between groups before the experiment started. Such biases can arise from random chance or from an issue in the random assignment process.
{% figure %}

{% /figure %}
### Raw data
This view shows the group-level statistics needed to compute the metric deltas and confidence interval. These include Units, Mean, and Total (described above), as well as the Standard Error of the mean (Std Err). Refer to the [statistical calculations reference](/experiments/statistical-methods/introduction) for details.
### Impact
{% figure %}

{% /figure %}
* **Experiment Delta (absolute)**: The absolute difference of the Mean between test groups i.e. Test Mean - Control Mean. Statsig shows the p-value to indicate whether the observed absolute difference is statistically significant.
* **Experiment Delta (relative)**: Relative difference of the Mean i.e. 100% x (Test Mean – Control Mean) / Control Mean.
* **Topline Impact**: The measured effect that experiment is having on the overall topline metric each day, on average. Computed on a daily basis and averaged across days in the analysis window. The absolute value is the net daily increase or decrease in the metric, while the relative value is the daily percentage change.
* **Projected Launch Impact**: An estimate of the daily topline impact Statsig expects if a decision is made and the test group is launched to all users. This takes into account the layer allocation and the size of the test group. Assumes the targeting gate (if there is one) remains the same after launch.
Refer to [topline and projected impact calculations](/experiments/statistical-methods/topline-impact) for details.
**FAQs about topline impact**
*Why is the projected launch impact smaller than the relative experiment delta?*
An experiment can impact only a subset of the user base that contributes to a topline metric. The relative experiment delta observed is effectively diluted when measured against the topline metric value.
For example: consider a top-of-funnel experiment on the registration page. Among users who visit this page, the treatment leads to more sign-ups and a 10% lift in daily active users (DAU). However, the topline DAU metric includes other user segments outside of the experiment, such as long-term users who don't visit the registration page. A 10% lift in the test vs. control comparison may amount to only a 1% increase in overall DAU.
*How can the topline impact be higher than the experiment delta?*
The topline impact can be higher or lower than the experiment delta because Statsig computes the two values differently and they have different meanings.
Experiment deltas are based on unit-level averages: Statsig computes the mean value of the metric for each user across all days, then averages it to obtain the group mean. Statsig computes the topline impact daily based on the total pooled effect from all users, then averages it across days to show the daily impact.
Statsig computes topline impacts this way because most metrics are tracked on a daily basis and the topline value is an aggregation across all users rather than a user-level average. For experiment analysis, best practice is for the analysis unit to match the randomization unit, so metrics are aggregated at the unit level first before computing experiment deltas.
================================================================================
# Exporting Pulse Reports
URL: https://docs.statsig.com/experiments/interpreting-results/export
Updated: 2026-07-11
================================================================================
## How to Export Pulse Data in Statsig Cloud
{% callout type="tip" %}
**Warehouse Native users**: You're viewing the Cloud docs for this page. Metrics and experiments behave differently in Warehouse Native. Read [How to Access Pulse Data in Warehouse Native](/experiments/interpreting-results/access-whn).
{% /callout %}
{% figure %}

{% /figure %}
To export Pulse Results for Feature Gates and Experiments, go to the relevant "Pulse Results" page and click **Export Report**. Exporting results can take up to 10 minutes. Statsig sends you a notification and an email when the report is ready, and a link appears under *Project Settings > Reports*. You can export results only if your Pulse screen has results.
{% figure %}

{% /figure %}
================================================================================
# Best Practices and Avoiding False Positives
URL: https://docs.statsig.com/experiments/interpreting-results/best-practices
Updated: 2026-07-11
================================================================================
The following suggestions help you interpret Pulse in a scientifically sound way:
1. Have a hypothesis in mind before viewing Pulse. What metrics do you expect to shift due to the change you made? What else could have happened? What are signs that something went wrong?
2. Establish a small set of key metrics directly related to your hypothesis that would establish that the experiment worked. More than a handful of key metrics usually indicates an ill-defined hypothesis or unfocused experimentation. Examining too many metrics increases the false positive rate (seeing results when only statistical noise exists).
3. Avoid cherry-picking results. Don't selectively pick three metrics that look good while ignoring two that don't. Also avoid picking "good" or "bad" numbers with no connection to your hypothesis. Context matters: statistically significant results should have a plausible explanation (a false positive is a plausible explanation).
4. Multiple independent effects that are consistent with a plausible story lend credibility to the observed effects, even with borderline p-values.
5. Expect to see false positives and be suspicious of statistically significant results with borderline p-values. For example, a 95% confidence interval (5% significance level) is expected to produce one statistically significant metric out of twenty due purely to random chance. This number increases when you include borderline metrics (for example, p = 0.06).
6. Look beyond your hypothesis. What other effects can you find? Are there tradeoffs? Are there unexpected behaviors? These can reveal information about your users and how they interact with your product, and are often the source of follow-up experiments and new ideas.
================================================================================
# Reconciling Results Between Experimentation Platforms
URL: https://docs.statsig.com/experiments/interpreting-results/reconciling-experiment-results
Updated: 2026-07-11
================================================================================
## Why results differ between platforms
The same data can yield very different interpretations in experiment results due to the wide variety of analysis methodologies available. One advantage of modern experimentation platforms is ensuring consistency and transparency in experimental analysis within your organization. This guide covers common gaps between platforms and how to identify and resolve them.
## General approach
When companies evaluate an experimentation vendor, differences in results between their in-house platform and the vendor's platform are common during Proof-of-Concept (POC) validations. You can typically resolve these gaps by working through the following hypotheses in order:
1. The metric source data is being read or joined to exposure data differently, invalidating downstream steps.
2. Advanced statistical features available on the vendor side but not in-house are working as intended, most often reducing the influence of outliers or pre-experiment bias.
3. There is a misunderstanding of how a metric definition works, or how an advanced configuration on a metric or experiment behaves.
By working through these in order, data teams can quickly understand and address gaps, or decide whether the vendor's approach is acceptable.
## Joining data
Based on observational data, differences in experiment results most often stem from how exposure data is joined with metric data. At the end of this section, there is a basic check for confirming this isn't occurring.
### ID formats
In some cases, IDs are logged in different formats to different places. For example, the binary ID `4TLCtqzctSqusYcQljJLJE` maps to the UUID `a0fb4ef0-9d9e-11eb-9462-7bfc2b9a6ff2`, so a company might have the binary ID in their production environment while their data users work with the equivalent UUIDs.
Exposures logged using the binary ID can't join with metric data using the UUID, and results are empty. Check samples for both the metric source and the assignment source or diagnostic logstream to confirm that the identifiers are in the same format.
You can use ID Resolution to bridge ID type gaps, but it isn't intended to solve this scenario. ID Resolution helps connect identifiers across logged-out/logged-in sessions, or other scenarios where users commingle their identifiers by switching identifiers during the experiment.
### Timestamps
Analyze metric data only after a user has been exposed to the experiment. Pre-experiment data should have no average treatment effect, so its inclusion dilutes results.
#### Statsig Cloud
Statsig Cloud uses a date-based join between exposures and metric data. Experiments include metric data from the whole of the first exposure date for each experimental unit. While some pre-experiment metric data can be included, the average treatment effect of this dilution should be null. This is illustrated in the SQL snippet below:
```
WITH
metrics as (...),
exposures as (...),
joined_data as (
SELECT
exposures.unit_id,
exposures.experiment_id,
exposures.group_id,
metrics.timestamp,
metrics.value
FROM exposures
JOIN metrics
ON (
exposures.unit_id = metrics.unit_id
AND metrics.date_id >= exposures.first_date_id
)
)
SELECT
group_id,
SUM(value) as value
FROM joined_data
GROUP BY group_id;
```
Statsig's exposures are always in UTC. If metric data is in another timezone, adjust it to avoid filtering on the wrong comparison.
Statsig does support timestamp-based joins for some Enterprise Cloud customers. Contact Statsig to learn more.
#### Statsig Warehouse Native
Statsig WHN uses a timestamp-based join, with an option for a date-based join for daily data. This is illustrated in the SQL snippet below:
```
WITH
metrics as (...),
exposures as (...),
joined_data as (
SELECT
exposures.unit_id,
exposures.experiment_id,
exposures.group_id,
metrics.timestamp,
metrics.value
FROM exposures
JOIN metrics
ON (
exposures.unit_id = metrics.unit_id
AND metrics.timestamp >= exposures.first_timestamp
)
)
SELECT
group_id,
SUM(value) as value
FROM joined_data
GROUP BY group_id;
```
Timestamps for Statsig's exposures are always in UTC. If metric data is in another timezone, adjust it to avoid filtering on the wrong comparison.
### Exposure duplication
De-duplicate exposure data before joining to ensure a single record per user. Many vendors also manage crossover users (users present in more than one experiment group), removing them from analysis or alerting when crossovers occur at high frequency.
```
SELECT
unit_id,
experiment_id,
MIN(timestamp) as first_timestamp,
COUNT(distinct group_id) as groups
FROM
GROUP BY
unit_id,
experiment_id,
group_id
HAVING COUNT(distinct group_id) = 1;
```
### Data availability
When comparing a platform analysis to an **existing** experiment analysis that ran in the past, the underlying data may have fallen out of retention or been deleted. Compare the table's retention policy to the analysis dates used in your original experiment analysis to confirm that the data still exists. Also confirm that your experiment in the vendor console is configured to analyze the same time range as your original analysis.
### Validation
To validate the initial metric data and join, use the query provided in the [Timestamps](/experiments/interpreting-results/reconciling-experiment-results#timestamps) section, modifying it to run on both platforms. Confirm that a target metric has the same totals per group across both platforms.
Warehouse Native platforms have an advantage here because the SQL dialect and source data are generally the same in both vendor code and in-house code, making comparisons simpler.
Pick one metric of interest, validate that data, and resolve any differences before checking statistical and metric methodologies.
## Statistical features
Choices in statistical methodologies can significantly impact experiment results. The following are common root causes for gaps in results. Always closely read the queries being run by the vendor to understand any particulars in methodology.
### Winsorization
Outlier trimming, or [Winsorization](/experiments/statistical-methods/methodologies/winsorization), can dramatically alter experiment outcomes. Disable this feature in Statsig metrics when doing cross-system comparisons unless you're also applying it manually.
### CUPED
[CUPED](/experiments/statistical-methods/methodologies/cuped) can significantly change variances and observed deltas, especially with high pre- and post-exposure data correlation or systematic differences in groups' pre-experiment data.
You can configure CUPED at a metric level. You can also disable it for a pulse result set after running analysis.
### Ratio metrics
For ratio metrics using the delta method, Statsig includes only units with a non-zero denominator. Statsig calculates ratios and means as
$\bar\{u} = \frac\{\sum\_\{i=0}^\{n}(numerator\_i)}\{\sum\_\{i=0}^\{n}(denominator\_i)}$
and uses the [delta method](/experiments/statistical-methods/variance#ratio-and-mean-metrics) to correct for the cluster-based nature of these metrics.
## Metric definitions
Users often misunderstand how a given metric is calculated. Refer to the comprehensive [metrics guide](/statsig-warehouse-native/configuration/metrics) for details.
## Summary
Following these steps clarifies where any gaps between two experiment platforms are coming from. Statsig provides the intermediate and result datasets it uses, as well as the queries used in its analysis, making it straightforward to understand where gaps arise.
If you get stuck, reach out for help. For an overview of experiment pipeline patterns, refer to the [Statsig Warehouse Native Documentation](/statsig-warehouse-native/introduction) and [Statsig Pipeline Overview](/statsig-warehouse-native/analysis-tools/pipeline-overview/).
================================================================================
# Frequently Asked Questions on Using Pulse
URL: https://docs.statsig.com/experiments/interpreting-results/faq
Updated: 2026-07-11
================================================================================
This page provides answers to common questions about interpreting statistical results.
## I had a stat sig result, but it turned negative. How should I interpret this
Trust the current result, as it incorporates more information about the users in your experiment.
This can happen for several reasons:
* Random noise, which is reduced as sample size grows
* Within-week seasonality (for example, an effect that differs on Mondays), which normalizes with more data
* The population that saw the experiment early is different from slower adopters. This is common: a daily user will likely see your experiment before someone who uses your product once a month. Use the time series view for more insight.
* A novelty effect made the experiment appear meaningful early on, but the effect faded. For example, users might click a changed button out of curiosity, then revert to prior behavior after the novelty wears off. Use the days-since-exposure view for more insight.
Best practice for timing is to pick a readout date when you launch your experiment (based on a [power analysis](/experiments/power-analysis)), and to disregard the statistical interpretation of results until then. Reading results multiple times before the readout date dramatically increases the rate of false positives.
## How should I start with interpreting results
Start by using your scorecard metrics to understand whether you moved the metrics you expected to move. Bring a hypothesis about what your experiment should drive; your primary metrics should answer that hypothesis.
The delta displayed is based on the observed difference between a test and control population. The error bars visualize a confidence interval: a range of probable values for the difference between groups. A future sample's 95% confidence interval has a 95% chance of containing the true value of the difference. In practice, the CI is a representative range of what the true value might be.
These results are statistical interpretations, not facts:
* If a result isn't stat sig, you don't have sufficient evidence to reject the null hypothesis (i.e., based on your experiment design the observed result is reasonably likely to have happened by chance).
* Generally, treat these results as a lack of evidence for your hypothesis.
* Underpowered tests may lead to neutral results even if a true effect exists.
* If a result is stat sig, you have sufficient evidence to reject the null hypothesis (i.e., the probability that you would observe this result, or one more extreme, if the two groups' results were identical is below the pre-determined threshold you set).
* Generally, treat this result as evidence for your hypothesis.
* Multiple comparisons (many metrics, rerunning an experiment, or grouping by dimensions) greatly increase the chance of seeing a stat sig result when there's *not* a true effect. Be wary of interpreting results when you see those behaviors!
* A test that was extremely unlikely to succeed and has a stat sig result has a high chance of being a false positive. Consider trying to reproduce the result, running a back-test, or reducing your significance level.
After reviewing the scorecard results, use the all-metrics tab and custom queries to gather more information. Stat-sig movements in those views aren't necessarily statistically sound: increasing the number of metrics you examine increases the chance of a false positive. Use this section to look for unexpected large regressions and to generate follow-up hypotheses.
## Results aren't showing up for some metrics
This normally happens when your company uses the SDK or event imports and also imports precomputed metrics from your data warehouse. Because these can run at different times, data availability may differ. Adjust your analysis date range to get a full view of your data.
## Our external source shows more exposure events than Statsig. Are data missing
Statsig doesn't count exposures on the last day (the day you made a decision). Filter out that day when you analyze your external data. The hours that define a "day" for your project depend on which timezone your project uses.
## We log categorical metadata for a custom event, but Pulse doesn't show these breakouts. What's wrong
Pulse can show experimental results for sub-groups of your metric (for example, iOS vs. Android) only when you configure your metadata as a Dimension. [Value Dimensions](/experiments/interpreting-results/read-results#value-dimensions) are the most common dimension type, as their metadata is logged directly with your custom events. Value dimensions must be defined in your [custom event setup](/metrics/metric-dimensions).
## Why do I see "No dimensions available for this time range"
You may see this error when trying to view precomputed user dimensions, particularly after the first reload of the day. This happens because:
* Dimensions load asynchronously in separate explore queries after the main scorecard results load.
* The main experiment results appear first, while dimensions continue loading in the background.
* Typically, dimensions become available within a few minutes after the main scorecard loads.
If you encounter this error, wait a few minutes and refresh the page to check whether the dimensions have completed loading.
================================================================================
# Participating Units
URL: https://docs.statsig.com/experiments/interpreting-results/participating-units
Updated: 2026-07-11
================================================================================
## What are participating units
In Statsig, Participating Units (sometimes referred to as participating users) are a subset of an experiment's total exposed unit count relevant to a particular metric. Participating Units are the units with a non-zero denominator value for metric types that require a numerator and denominator value. Statsig uses these units in statistical calculations for ratio-based metrics.
{% figure %}

{% /figure %}
Statsig computes ratio metrics only for users that have a non-zero value in the denominator: the user must have triggered the denominator event on a given day to be included in the daily ratio. Users that don't trigger the denominator event during an experiment are excluded from the test vs. control comparison of a ratio metric.
## Example
An example of this type of metric for an e-commerce company could be "Total Items Purchased per Order", measuring the average basket size. The numerator would be defined as the count of total items bought, and the denominator would be the total number of orders submitted. If an experiment were run on the checkout flow, only users who actually had at least one Order event would be included in the "Total Items Purchased per Order" ratio metric.
================================================================================
# Custom "Explore" Queries
URL: https://docs.statsig.com/experiments/interpreting-results/custom-queries
Updated: 2026-07-11
================================================================================
Custom queries let you run additional experiment analyses on your existing data beyond what is in the main Results tab. Use them to gain deeper insights from your experiments and feature rollouts, debug interesting results, or narrow your results to specific sub-groups. Custom queries let you filter or group metrics by event or user dimensions, or filter to a specific set of users to see how an experiment or launch impacted those users.
Custom queries are experimental analyses identical to those in the main Results tab, and all the same statistical procedures apply. Statsig computes results as p-values and confidence intervals for your metric deltas, and you can use advanced statistical methods like [CUPED](/experiments/statistical-methods/methodologies/cuped) and [Sequential Testing](/experiments/advanced-setup/sequential-testing).
{% callout type="warning" %}
Be careful when drawing your inferences of Custom Queries, especially when grouping by a dimension with lots of options. This can increase your chance of seeing a false-positive statistically significant result.
{% /callout %}
### Dimension loading timing for precomputed user dimensions
When viewing results for precomputed user dimensions (which are configured and run on a schedule), be aware that these dimensions are loaded through separate asynchronous explore queries. This means:
- The main experiment results will appear first
- Precomputed dimensions will continue loading in the background and become available within a few minutes
- This timing gap is most noticeable immediately after the first reload of the day
- If you see "No dimensions available for this time range" for precomputed dimensions, wait a few minutes and refresh to see if dimensions have completed loading
{% callout type="note" %}
This timing behavior only affects precomputed user dimensions that run on a schedule. User-triggered custom queries don't experience this asynchronous loading delay.
{% /callout %}
### Running a custom query
To run a Custom Query, navigate to the **Explore** tab within your experiment.
{% figure %}
{% /figure %}
Custom Query fields:
- **Metric(s):** The metric(s) you want to analyze. You can select a single metric, a few metrics, or a Metric Tag. Adding a Tag includes all the metrics within that Tag in your Custom Query. There are three "default" metric selections included as shortcuts:
- "Scorecard Metrics", all metrics included in your experiment setup's Primary and Secondary Metrics sections
- "Primary Metrics"
- "Secondary Metrics"
- **Metric Filter:** With metrics selected, you can filter metrics by either Event or User dimensions using the "Add Filter" dropdown. For example, if you wanted to look at your experiment results for Canadian users only, you could filter to "Country = CA".
{% figure %}

{% /figure %}
- **Group By:** You can group your Custom Query results by either an Event or User dimension. Whereas Custom Query filters can be applied at the _per-metric_ level, the Group By action is at the _query_ level (so all included metrics will have whatever Group By you select applied to them).
- **Time Range for Metric Data:** The date range you're running your analysis on. By default this is the "Full date range" of your experiment data.
- **(Advanced) ID List Segment filters:** You can choose an ID-list based [Segment](/segments/overview), and Statsig calculates results only for users who are in that segment. This can be useful if you forgot to log an important user dimension that you want to filter to, or realized that you only care about a sub-population that you've defined in your own data warehouse.
- Careful! This option can easily lead to erroneous and biased results. Make sure the segment is defined based on the user's status _before_ they were exposed to the experiment or feature gate.
- Similarly, you can choose to _exclude_ a certain ID list segment, for example if you want to exclude a set of users who have been retroactively identified as bad actors from your lifts analysis.
- **(Advanced) Filter by Exposure Date:** You can also filter the results by Exposure Date which can give you more flexibility. You can choose only include or exclude a date range, or in WHN, you can additionally include/exclude users based on when they were first exposed to the experiment.
- This is useful when your metrics have novelty effect, delayed impact, or specific scenarios where you only want to filter your results to certain users. Use it cautiously because it can lead to biased results.
{% callout type="note" %}
User groups in experiment results are based off of first-touch attribution. Statsig bases the filters and grouping on the user attributes collected at the time of first exposure in the gate/experiment/layer check.
{% /callout %}
{% figure %}
{% /figure %}
### Viewing a custom query in Explore
These queries take a few minutes to run. Statsig sends an email when your results are ready. After the query completes, results are visible in the **Query History** section of the **Explore** interface. All historical queries across your team are stored here. You can also give your query a display name inline for easier future identification.
{% figure %}
{% /figure %}
### Scheduling a custom query
To get a daily refresh of a given Custom Query, schedule it directly from the **Explore** tab. Author the Custom Query you want to schedule, then tap the "..." menu, then **Schedule**. The Custom Query now runs daily and lives in the **Scheduled** tab of your Metric Lifts.
{% figure %}
{% /figure %}
{% figure %}
{% /figure %}
### Reviewing custom query results
Custom query results look similar to the main Results tab because the statistical methods are the same. Statsig applies the same experimental analysis practices to custom analyses as to main Results.
The key difference is that a custom query result is a snapshot in time. After the query runs, results are saved and don't update if more metric data is collected. To update your results, run a new custom query or schedule custom queries to run at a regular cadence.
#### Sequential testing and custom queries
If [Sequential Testing](/experiments/advanced-setup/sequential-testing) is enabled for your experiment, you can apply it to your custom query results as well. Whether and how much to adjust confidence intervals and p-values depends on the standard rules of sequential testing: if your custom query doesn't satisfy the experiment's target Days or Unique Exposures from your setup, Statsig makes sequential testing adjustments to account for the under-powered state of the experiment.
Because custom queries are computed as a snapshot in time, sequential testing adjustments are computed for that specific analysis only. If you run additional custom queries with more or less data (for example, more days in the analysis or more unique users in the experiment), the sequential testing adjustments change accordingly. Custom queries that meet the configured minimum Days or Unique Exposures have no sequential adjustments applied.
{% figure %}

{% /figure %}
================================================================================
# Metric Insights and Aggregated Impact
URL: https://docs.statsig.com/experiments/exploring-results/aggregated-impact
Updated: 2026-07-11
================================================================================
## Metric Insights and Aggregated Impact
Statsig's Insights page provides a clear view of how experiments and feature gates impact a specific metric. It helps answer key questions such as "How much impact have I driven?" and helps diagnose unexpected changes in metrics.
Insights presents a reverse perspective of the [Pulse](/experiments/interpreting-results/read-results) view. While Pulse measures the impact of a new feature on all your metrics, Insights lets you focus on a single metric and identify which tests are impacting it the most. This is useful for assessing your team's impact and setting realistic goals.
## How to read Insights
1. Navigate to the Insights section on the Statsig console: https://console.statsig.com/ . Insights is also available in the insight tab for each metric.
2. Select a metric from the selector drop-down at the top of the page.
3. Select the ID type, time window, and other filters.
4. The page shows the relative impact, topline impact, and projected launch impact for any experiment or gate that includes this metric.
5. Statsig sums the projected launch impacts, adjusts based on false positive risk ('winner's curse'), and displays the result as the Aggregated Impact Estimate.
{% figure %}

{% /figure %}
## How the math works
Go to the [topline and projected launch impact calculations](/experiments/statistical-methods/topline-impact#computing-projected-launch-impact) for details.
To estimate false positive risk and calculate Aggregated Impact, Statsig uses the methodology in this [paper](https://dl.acm.org/doi/10.1145/3534678.3539160), which is widely adopted across the industry. Specifically:
$$
Aggregated Impact=\sum\_\{i}\{(1 - FPR\_i) \times Projected Launch Impact\_i}
$$
The [projected launch impact](/experiments/statistical-methods/topline-impact) is an estimate of the topline impact assuming a decision is made and the test group is launched to all users. The false positive risk is calculated by the following formula:
$$
FPR\_i = \frac\{\alpha\_i \times \pi}\{\alpha\_i \times \pi + (1 - \beta\_i) \times (1 - \pi)}
$$
In this formula, $\alpha\_i$ is the significance level for experiment i, $\beta\_i$ is the type II error, and 1 - $\pi$ is the prior success rate based on historical experiment results.
================================================================================
# Topline and Projected Impact
URL: https://docs.statsig.com/experiments/statistical-methods/topline-impact
Updated: 2026-07-11
================================================================================
The **topline impact** is the average daily effect that an experiment has on the overall metric value between two groups. This is the actual daily impact to a metric resulting from running the experiment, measured across the two groups being evaluated. The **projected launch impact** is an estimate of the daily impact expected in the metric measured globally if the test group is launched to all users (beyond those in the experiment). Statsig computes this impact relative to the expected baseline value of the metric without the experiment running.
Statsig shows topline impact and projected launch impact in both absolute and relative units. Neither uses CUPED when measuring the impact of your experiment, because CUPED already adjusts for pre-exposure data: the same data that topline metrics change from. Combining them would double-count that adjustment.
{% callout type="tip" %}
**Example**: Take a simple example experiment with a Control group of 1000 users and a Test group of another 1000 users, which ran for 30 days. For an **event\_count** metric, the Experiment Delta is +1.0 events per user (abs). The Topline Impact for this metric is +33.33 events per day (abs).
{% /callout %}
## Computing topline impact
Statsig computes the topline impact over the total duration of the experiment, which gives the most accurate estimate and a tight confidence interval. The calculation depends on whether the metric represents an absolute quantity or a ratio:
### Count and sum metrics (event\_count, sum)
Statsig derives the absolute topline impact directly from the experiment results. It depends on the difference in means between test and control, and the average number of users in the test group per day.
$$
Impact\_\{abs}=(X\_t-X\_c) \cdot N\_t / n\_\{days}
$$
Using the absolute impact and the overall metric value (as seen in the [metrics dashboard](/metrics/console)), Statsig computes the relative impact. This is the percentage change in the overall metric value over the rollup window attributed to the active experiment.
$$
Impact\_\{rel}=\frac\{Impact\_\{abs}}\{Topline\_Value-Impact\_\{abs}} \times 100\\%
$$
### Ratio and mean metrics
To derive the topline impact on a ratio metric, Statsig first determines the impact on the numerator (*X*) and denominator (*Y*) separately. The topline impact is the current value of the ratio metric minus the baseline value obtained by subtracting the numerator and denominator impacts:
$$
Impact\_\{abs}=\frac\{Topline\_X}\{Topline\_Y}-Baseline\_Value
$$
Where the baseline value is the expected value of the topline metric if the experiment wasn't running:
$$
Baseline\_Value=\frac\{Topline\_X-(\bar\{X\_t}-\bar\{X\_c}) \cdot N\_t}\{Topline\_Y-(\bar\{Y\_t}-\bar\{Y\_c}) \cdot N\_t}
$$
Statsig computes the relative impact for ratio metrics by dividing the absolute impact by the baseline value:
$$
Impact\_\{rel}=\frac\{Impact\_\{abs}}\{Baseline\_Value} \times 100\\%
$$
## Computing projected launch impact
Statsig uses the layer allocation of the experiment and the size of the test group to estimate a scaling factor *m*, which represents the increase in absolute impact expected when the test group is launched.
Statsig calculates the launch factor over a rollup window as
$$
m\_\{rollup}=\frac\{1}\{\sum\_\{1}^\{rollup}\{layer\_alloc \times group\_pct}} \times rollup
$$
to accommodate changes in allocation during the experiment.
{% callout type="note" %}
The targeting gate isn't factored in. The projected impact calculation assumes that the target gate remains the same after the experiment is launched.
{% /callout %}
### Count and sum metrics (event\_count, event\_dau, sum)
For count and sum metrics, the projected absolute impact is the current topline impact scaled by *m*. For example, consider an experiment running with 50% layer allocation and a 50/50 test/control split, so that 25% of all users are in the test group. If the allocation changed during the experiment, Statsig uses a weighted average based on historical allocations. If the topline impact is currently +10 events per day, then launching the experiment would result in +40 events per day.
$$
Projected\_\{abs}=Impact\_\{abs} \times m
$$
The relative projected impact is the expected percentage change in the topline metric, relative to the baseline value of the metric without the experiment running.
$$
Projected\_\{rel}=\frac\{Projected\_\{abs}}\{Topline\_Value-Impact\_\{abs}} \times 100\\% = Impact\_\{rel} \times m
$$
### Ratio and mean metrics
The projected impact of ratio metrics depends on the numerator and denominator impacts, using the same scaling factor *m* to obtain the projected impact for each term:
$$
Projected\_\{abs}=\frac\{Topline\_X+(m-1) \cdot (\bar\{X\_t}-\bar\{X\_c}) \cdot N\_t}\{Topline\_Y+(m-1) \cdot (\bar\{Y\_t}-\bar\{Y\_c}) \cdot N\_t} - Baseline\_Value
$$
Where the first term represents the projected metric value after launch.
Finally, the projected relative impact of a ratio metric is the projected absolute impact divided by the baseline value of the ratio:
$$
Projected\_\{rel}=(\frac\{Projected\_\{abs}}\{Baseline\_Value}) \times 100\\%
$$
## Confidence intervals
Statsig computes the confidence intervals for topline and projected impact using the same method as [confidence intervals for experiment deltas](/experiments/statistical-methods/confidence-intervals).
$$
CI(Impact) = Impact \pm Z \cdot \sqrt\{var(Impact)}
$$
In the case of absolute impact of count and sum metrics, the variance calculation is simply a linear combination of the test and control variances:
$$
var(Impact\_\{abs})=\[var(\bar\{X\_t})+var(\{\bar\{X\_c}})] \cdot N\_t^2
$$
For projected launch impact:
$$
var(Projected\_\{abs})=var(Impact\_\{abs}) \cdot m^2
$$
For ratio metrics and relative impacts, Statsig calculates the variance using the Delta method. This accounts for the correlation between numerator and denominator terms, using Taylor expansion to linearize expressions containing non-linear combinations of experiment variables.
For example, the variance in the relative impact of a count metric is given by:
$$
var(Impact\_\{rel})=var(Impact\_\{abs}) \cdot \frac\{(Topline\_Value - 2 \cdot Impact\_\{abs})^2}\{(Topline\_Value - Impact\_\{abs})^4}
$$
================================================================================
# Meta-Analysis
URL: https://docs.statsig.com/experiments/exploring-results/meta-analysis
Updated: 2026-07-11
================================================================================
## How meta-analysis works
As teams run many experiments, patterns emerge across those experiments. Meta-analysis surfaces those cross-experiment insights. Common questions include:
- How hard is a metric to move?
- Are there more sensitive proxies for the metric you care about?
- How are teams doing relative to each other?
Statsig has built meta-analysis to be useful whether you're running 50 experiments a year or 5000. Join [Slack](https://statsig.com/slack) to help influence the roadmap.
## Experiment timeline view
This view lets you filter down to experiments a team has run. At a glance you can answer questions like:
1. What experiments are running now?
2. When are they expected to end?
3. What percentage of experiments ship Control vs Test?
4. What is the typical duration?
5. Do experiments run for their planned duration, or much longer or shorter?
6. Do experiments impact key business metrics, or only shallow or team-level metrics?
7. How much do they impact key business metrics?
{% figure %}

{% /figure %}
## Metric impact (batting average)
The "batting average" view shows how easy or hard a metric is to move. Filter to a set of shipped experiments and see how many experiments moved a metric by 1% vs 10%. You can filter by team, tag, or statistical significance. Common uses include:
- Validating whether a claim that the next experiment will move this metric by 15% is reasonable.
- Establishing realistic goals based on past ability to move this metric.
{% figure %}

{% /figure %}
## Metric correlation view
This view lets you visualize two metrics and inspect them for correlation. Each data point represents one experiment's impact on both metrics.
Often the metric you want to move isn't very sensitive and takes a while to measure. Finding more sensitive, faster-to-measure proxy metrics and running experiments on those proxies can accelerate your work.
You can remove outliers, filter to a team's experiments, or download the underlying dataset.
In this hypothetical example, "Checkouts" is the metric you want to move, but it isn't very sensitive. "AddToCart" correlates well with "Checkouts", while "ViewItemDetail" doesn't.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
## Metric insights
This view lets you pick a metric and see all experiments and feature rollouts that impact it. [Learn more](/experiments/exploring-results/aggregated-impact).
{% figure %}
{% /figure %}
## Knowledge Bank
The Knowledge Bank is a searchable repository of experiment learnings across teams. It helps you find shipped, healthy experiments, gain context on past work, and generate ideas for new experiments.
New team members can explore experiments a team ran or search by topic. The meta-analysis tools offer more structured ways to discover and review your experiment corpus. The Knowledge Bank is available when you need free-text search.
{% figure %}

{% /figure %}
================================================================================
# Differential Impact Detection
URL: https://docs.statsig.com/experiments/exploring-results/differential-impact-detection
Updated: 2026-07-11
================================================================================
{% partial file="whn/differentialImpact.mdx" /%}
================================================================================
# Interaction Detection
URL: https://docs.statsig.com/experiments/exploring-results/interaction-detection
Updated: 2026-07-11
================================================================================
## What is interaction detection
When you run overlapping experiments, the experiments can interfere with each other. Interaction Detection lets you pick two experiments and evaluate them for interaction. This helps you understand whether users exposed to both experiments behave differently from users exposed to only one. The core question is: **Does being in both experiments change the effect on a metric in a way that differs from summing the individual effects?**
## Should I worry about it?
The general guidance is to run overlapping experiments. Users viewing your landing page may experience multiple experiments at the same time. This is [supported by industry experience](https://www.statsig.com/blog/embracing-overlapping-a-b-tests-and-the-danger-of-isolating-experiments) and [published research](https://www.microsoft.com/en-us/research/articles/a-b-interactions-a-call-to-relax/). Teams expecting to run conflicting experiments can avoid conflicts by making experiments mutually exclusive using [Layers](/experiments/layers-overview) (also referred to as Universes).
## How to use interaction detection
To start an Interaction Detection analysis, go to **Experiment > Results > Explore > Interaction Effect Detection**.
{% figure %}

{% /figure %}
Select a second experiment and the metrics to analyze. Statsig then shows a summary with three sections:
* **By Groups**: unit counts based on the collective slice of group assignment
* **Metric Summary Results**: estimated intervals of difference in metric lift
* **Overlapping Unique Users**: an overview of the general traffic intersection of the two experiments
{% figure %}

{% /figure %}
Refer to this [article](https://www.statsig.com/blog/interaction-effect-detection) for additional examples.
## Methodology
Assume you have two experiments A and B, each with two groups: control and test. The interaction effect calculates the overlapping impact on users exposed in both experiment A and experiment B.
$$
\Delta\_\{\text\{treatment effect}} = (\overline\{X\_\{testA \cdot testB}} - \overline\{X\_\{testA \cdot controlB}}) - (\overline\{X\_\{controlA \cdot testB}} - \overline\{X\_\{controlA \cdot controlB}})
$$
$$
\Delta\_\{\text\{treatment effect}}\{\\%} = \frac\{\Delta\_\{\text\{treatment effect}}}\{(\overline\{X\_\{controlA \cdot testB}} - \overline\{X\_\{controlA \cdot controlB}})}
$$
$$
variance = \frac\{Var(X\_\{testA \cdot testB})}\{n\_\{testA \cdot testB}} + \frac\{Var(X\_\{testA \cdot controlB})}\{n\_\{testA \cdot controlB}} + \frac\{Var(X\_\{controlA \cdot testB})}\{n\_\{controlA \cdot testB}} + \frac\{Var(X\_\{controlA \cdot controlB})}\{n\_\{controlA \cdot controlB}}
$$
### Intuition
* The part $\overline\{X\_\{testA \cdot testB}} - \overline\{X\_\{testA \cdot controlB}}$ answers: “If someone is in A’s test, how much does changing B from control to test change the metric?”
* The part $\overline\{X\_\{controlA \cdot testB}} - \overline\{X\_\{controlA \cdot controlB}}$ answers: “If someone is in A’s control, how much does changing B from control to test change the metric?”
* Subtracting those two values shows whether the effect of B differs depending on whether the user is in A’s test or control group. That difference is the interaction effect.
If the result isn’t statistically significant, the effect of B is the same regardless of which group the user is in for A (no significant interaction). If it is statistically significant, the effect of B depends on A’s assignment (significant interaction).
### Directionality
The direction (positive or negative) of the interaction effect indicates whether the combination of the two treatments amplifies or dampens each other’s effects.
The interaction effect is defined as:
$Interaction Effect=(Effect of B when A=Test)−(Effect of B when A=Control)$
- **Statistically positive interaction**: B’s effect is stronger when A is also active.
- **Statistically negative interaction**: B’s effect is weaker (or reversed) when A is active.
### Magnitude
The magnitude (absolute size) of the interaction effect indicates how much the combined impact deviates from simple additivity.
For example:
* Experiment A produces a **+5%** lift on your metric of interest
* Experiment B produces a **+5%** lift on your metric
* The interaction effect between A and B is **–3%**
This means that when both experiments are rolled out together, their overall impact is expected to be about 3% lower than the sum of their individual effects: the features interfere with each other.
To obtain the most accurate estimate of the true combined impact, run a new experiment that includes both features together. Because experiments A and B may not have run over exactly the same time period or under identical conditions, differences in timing and seasonality can slightly influence the measured interaction magnitude.
{% callout type="info" %}
When an experiment includes more than two groups, interaction effects are evaluated **pairwise between groups**. You can view the interaction effect for specific group combinations by selecting the groups you want in the **”Select Comparison”** dropdown, as shown in the UI above.
{% /callout %}
================================================================================
# Layers
URL: https://docs.statsig.com/experiments/layers-overview
Updated: 2026-07-11
================================================================================
## What are layers
Layers (also known as Universes) allow you to create experiments that are mutually exclusive. Each layer has a logical representation of all your users and can have experiments created within it. Users assigned to one experiment in a layer cannot be in another experiment in the same layer.
{% figure %}

{% /figure %}
You can add experiments to a layer (or create a layer) during experiment creation.
{% figure %}

{% /figure %}
After you create a layer, you can manage layers on the layer management tab under Experiments.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
Layers also improve engineering efficiency and iteration velocity for product teams. In a Layer, parameters exist at the Layer level and can be shared across experiments within the Layer. This allows the concept of "Experiment" to be abstracted away from the SDKs, so that users only need to work with parameters in code. Multiple experiments that change the same thing can then run and iterate without any code changes.
Suppose your product has an important signup dialog with text that your team tests frequently. Some tests run in parallel, others are iterations of previous experiments. Working directly with Experiments, your code grows like this over time:
```jsx
let signUpText = DEFAULT_SIGNUP_TEXT;
const signUpTestV1 = statsig.getExperiment("sign_up_dialog_text_test_v1");
const signUpTestV2 = statsig.getExperiment("sign_up_dialog_text_test_v2");
const specialSignUpTest = statsig.getExperiment("sign_up_test_special_offer");
const holidaySignUpTest = statsig.getExperiment("sign_up_test_holiday");
if (signUpTestV1.get("is_in_test", false)) {
// original test, added in app version v1.2
signUpText = signUpTestV1.get("dialog_content", DEFAULT_SIGNUP_TEXT);
} else if (signUpTestV2.get("is_in_test", false)) {
// v2 of the original test, added in app version v1.6 because we wanted to test a new copy but don't want to stop v1
signUpText = signUpTestV2.get("dialog_content", DEFAULT_SIGNUP_TEXT);
} else if (specialSignUpTest.get("is_in_test", false)) {
// test showing a special offer in the text, added in v2.0
signUpText = specialSignUpTest.get("dialog_content", DEFAULT_SIGNUP_TEXT);
} else if (holidaySignUpTest.get("is_in_test", false)) {
// test showing some holiday greetings in the dialog, added in v2.1
signUpText = holidaySignUpTest.get("dialog_content", DEFAULT_SIGNUP_TEXT);
}
// Then we display the text in the dialog
```
Every time you add a new test, you must change the code, and the change is only available after a new release.
Working with Layers is much simpler:
```jsx
let signUpText = statsig
.getLayer("sign_up_tests")
.get("sign_up_dialog_text", DEFAULT_SIGNUP_TEXT);
// Then we display the text in the dialog
```
That is all the code you ever need. No more code changes and app releases for new tests. To add a new test, add a new experiment to the same Layer and choose `sign_up_dialog_text` as a parameter. The SDK determines which value to serve for the user based on which experiment the user is allocated to.
## getExperiment vs getLayer API
{% callout type="warning" %}
Even though layered experiments remain technically accessible through `getExperiment`, that API evaluates only the current experiment. Use `getLayer` so the SDK honors layer-level decisions, mutual exclusion, and shared parameters.
{% /callout %}
## How exposures work with layers
Calling `getLayer(LayerName)` by itself doesn't log an exposure. Statsig logs a `statsig::layer_exposure` event when you access a specific parameter within the Layer using `getLayer(LayerName).get(Parameter)`.
* If Statsig assigns the user to an experiment within the Layer, the `statsig::layer_exposure` event is billable.
* If the user isn't assigned to an experiment within the Layer, the `statsig::layer_exposure` event isn't billable.
{% callout type="note" %}
Repeated reads of the same Layer parameter for the same user within the deduplication window count as a single billable exposure.
Reads of different Layer parameters may count separately.
{% /callout %}
================================================================================
# Holdouts
URL: https://docs.statsig.com/experiments/holdouts-introduction
Updated: 2026-07-11
================================================================================
Holdouts measure the aggregate impact of multiple features. A holdout keeps a group of users back from a set of features for measurement. While each A/B test or experiment compares control and test groups for that feature, a holdout compares the holdout group (Control) against a balanced group of users who were not held out and continued through the normal rollout or experiment behavior for the included features.
## How to use holdouts
1. Navigate to the [Holdouts section on the Statsig console](https://console.statsig.com/holdouts) (a specialized kind of experiment) and click **Create New**.
2. Enter the name, description, and unit type for the holdout.
3. Choose a global or selected holdout. A global holdout is automatically added to any new feature with the same unit type, capturing the aggregate impact of all features developed after the holdout began (individual features may be opted out as needed). A selected holdout captures the aggregate impact of a specific set of features.
4. By default, holdouts apply to a percentage of all users (Population = Everyone). To target a subset of users, apply a Targeting Gate (Population = Targeting Gate). For example, to create an iOS-only holdout, apply a Targeting Gate that passes only iOS users.
5. Set the holdout percentage between 1% and 10%. Statsig recommends a small holdout percentage to limit the number of users who don’t see new features.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
## How to read holdouts
Holdouts use the [same “equal variant” methodology](/feature-flags/view-exposures#gate-exposures) as Feature Gate rollouts: Statsig computes metric lifts using equal-sized groups to calculate holdout lift. Go to [“A/B Testing Intuition Busters: Common Misunderstandings in Online Controlled Experiments”](https://drive.google.com/file/d/1oK2HpKKXeQLX6gQeQpfEaCGZtNr2kR76/view) by Ron Kohavi, Alex Deng, & Lukas Vermeer for more on the advantages of this methodology.
Accordingly, the Cumulative Exposures panel for a given Holdout shows total exposures of the Holdout, broken down into three groups:
1. **In holdout (Control)**: Units that were included in the Holdout and used for analysis.
2. **Not in holdout (Test); used for analysis**: Units that were not included in the Holdout and were selected for comparison against the holdout group.
3. **Not in holdout (Test); not used for analysis**: Units that were not included in the Holdout and were not used in the lift calculation.
For units not included in the Holdout, Statsig generates the two "Not in holdout" groups using random sampling. Statsig sizes the group used for analysis to balance the comparison against the holdout group.
Holdout metric lifts represent the cumulative impact of launched and active experiments on the Holdout group vs. the same percentage of the rest of the population continuing through normal behavior for the included rollouts and experiments. The "Not in holdout (Test); used for analysis" group isn't necessarily made up of users who saw every treatment. Those users follow the normal non-holdout behavior for each included gate or experiment.
In the example below, the 1% Holdout compares metric values of users in Holdout vs. 1% of users not in Holdout. The comparison doesn't include the full remaining 99% of users. The launched features are having an overall negative effect on the "Add to Cart" metric.
{% figure %}
{% /figure %}
## Best practices
1. **Size**: Statsig recommends a low single-digit holdout percentage, such as 1%–2%, to limit the number of customers who don't see new features.
2. **Duration**: Statsig recommends operating holdouts for three to six months, then releasing the holdout. Prolonging the holdout period may increase software complexity, because you must maintain a functioning product with no new features for a longer period.
3. **Back testing**: Occasionally you may want to turn off a set of already-released features to measure their effectiveness. Statsig doesn't recommend this approach because it turns off features that users are already using. However, when a "back measurement" is critical, you can use Holdouts to turn off a set of features and automatically compute their impact.
## Unit ID types
By default, holdouts are based on User ID. To use a different ID type, select it from the drop-down menu during holdout creation.
{% callout type="note" %}
Holdouts can only be applied to Experiments and Feature Gates that use the same randomization unit.
If a team plans to run experiments on both User ID and Stable ID, two separate holdouts are required to evaluate the cumulative impact of each type of experiment.
{% /callout %}
{% figure %}

{% /figure %}
## Holdout effects on gates & experiments SDK methods
### Feature flags/gates
- For users in holdout, gates always return `False`.
### Experiments
- For users in holdout, if the experiment _isn't in a Layer_, calls to get experiment parameters always return the "default value" passed in code.
- For users in holdout, if the experiment _is in a Layer_, calls to get experiment parameters return the values defined in the Layer defaults in the Statsig console.
{% callout type="note" %}
When you ship an experiment in a layer, this would normally update the layer defaults. However, users in the holdout will _not_ see those defaults. Instead, the layer has a separate set of default parameters only for held-out users:
{% /callout %}
{% figure %}

{% /figure %}
## Ending a holdout
To end a holdout and allow users in the holdout group to see all held-out features, disable the holdout. Disabling it stops tracking the effects of those features, but Statsig retains the results for future reference.
Alternatively, delete the holdout if it was created by mistake or if you no longer need to keep the results.
================================================================================
# Feature Flags
URL: https://docs.statsig.com/feature-flags/overview
Updated: 2026-07-11
================================================================================
**Feature Gates**, commonly known as feature flags, allow you to toggle the behavior of your product in real time without deploying new code.
Developers often use them to turn on certain features for a small percentage of the total user base. This approach enables safer, gradual software releases because you can monitor the impact on system behavior. Feature Gates also let you limit application behavior for a specific set of users, such as dogfooding [environments](/guides/using-environments).
{% figure %}

{% /figure %}
## When to use
#### Use when you need to
* Schedule gradual feature rollouts to safely deploy new code
* Set up dev staging environments before code hits production, like dogfooding
* Have an emergency disable switch that lets you immediately turn off a particular code branch for users in production
* Modify the user experience based on attributes like username, email, or other identifiers
* Change app behavior based on context like device, browser type, version, and other environment attributes
#### Not recommended if
* You need to return structured or multi-value data based on targeting rules and conditions: use a [Dynamic Config](/dynamic-config/overview) instead.
* You want to test complex hypotheses beyond simple A/B tests and launch impact: set up an [Experiment](/experiments/overview) instead.
## How it works
1. [Create a Feature Gate](/feature-flags/create) with [targeting rules](/feature-flags/conditions) in the Statsig console.
2. To have the Feature Gate impact users, integrate the [Statsig SDK](/sdks/getting-started) into your product code. The SDK queries the gate value at runtime and returns a true/false result based on user attributes, environment data, and other conditions you define.
3. [Test a Feature Gate](/feature-flags/test-gate) to confirm it behaves as expected before rolling it out.
4. For finer targeting control, set up [Feature Gate overrides](/feature-flags/overrides) to allow specific users to bypass the gate.
5. After your Feature Gate is live, [view Feature Gate exposures](/feature-flags/view-exposures) in the Statsig console to monitor who encounters your gate.
6. Set up deprecation rules and clean up old flags by [managing Feature Gate lifecycles](/feature-flags/feature-flags-lifecycle).
## Key capabilities
### Scheduled rollouts
Gradually deploy a feature over time by setting up a Feature Gate as a [Scheduled Rollout](/feature-flags/scheduled-rollouts).
### Overrides and bypass lists
Implement Feature Gates with [Overrides](/feature-flags/overrides) to allow a specific list of users to bypass your gate.
### Chained flag dependencies
Chain Feature Gates together in parent-child or other dependent relationships so a top-level gate can enable or disable all its dependent flags at once. This is useful for global disable switches that guard sub-features.
### Built-in A/B tests
Run simple A/B tests without additional setup using [Pulse](/feature-flags/view-exposures). In practice, this means treating the users who see the new feature as the "treatment" group, and the users who are gated (and therefore don't see the new feature yet) as the "control".
### Feature Gate testing
[Test your Feature Gates](/feature-flags/test-gate) with Statsig's built-in tools to check whether your Feature Gate is configured to target the right people.
## FAQs
{% accordion-group %}
{% accordion title="How are Feature Gates different from Dynamic Configs?" %}
Dynamic Configs are key-value configs that let you return structured data (not just true/false) based on targeting rules. They're more for customizing behavior, tuning parameters, or supporting complex logic *beyond booleans*. Feature Gates are simpler on/off switches for gating access to a feature. Technically, you can set up a Dynamic Config as a Feature Gate.
{% /accordion %}
{% accordion title="When should I use Feature Gates instead of Experiments (and vice versa)?" %}
Go to [Choosing Feature Flags vs. Experiments](/guides/featureflags-or-experiments) for a full guide. Use Feature Gates to measure the general impact of a feature rollout. Use Experiments when you have a more specific hypothesis or test in mind.
{% /accordion %}
{% /accordion-group %}
## Related tutorials
* [Set up dev environments with Feature Gates](/guides/using-environments)
* [Customize dev environments with Feature Gates](/guides/testing)
* [Choosing Feature Flags vs. Experiments](/guides/featureflags-or-experiments)
* [Best Practices for Feature Gates](/feature-flags/best-practices)
================================================================================
# Build Your First Feature
URL: https://docs.statsig.com/guides/first-feature
Updated: 2026-07-11
================================================================================
{% callout type="info" %}
Statsig refers to feature flags as feature gates across the console and SDKs. The terms are interchangeable throughout this guide.
{% /callout %}
After your Statsig account is ready, follow the steps below to create and test-drive a new feature gate.
{% steps %}
{% step title="Create a feature gate" %}
Navigate to the [Feature Gates page](https://console.statsig.com/gates) and click Get Started (or Create if you already have gates).
{% figure %}

{% /figure %}
Give the gate a clear name and description. For example, Mobile Registration with a note about the new mobile sign-up flow.
{% /step %}
{% step title="Target mobile platforms" %}
New gates default to returning false until you add targeting. Click Add New Rule, choose Operating System → Any of, and select Android and iOS. Set the pass percentage to 100% and click Add Rule, then Save.
{% figure %}

{% /figure %}
{% /step %}
{% step title="Add an internal testing rule" %}
Layer on a second rule for your team. For example, use Email → Contains any of with your company domain so employees can exercise the feature regardless of device.
{% figure %}

{% /figure %}
{% /step %}
{% step title="Generate a client API key" %}
Go to [Project Settings → API Keys](https://console.statsig.com/api_keys) and copy the Client API key. Keep server secret keys on backends only, and use console API keys for programmatic configuration work.
{% /step %}
{% step title="Load the JavaScript SDK" %}
{% callout type="tip" %}
Statsig supports many platforms. Refer to [Client SDK options](/sdks/getting-started) for alternatives. This walkthrough uses the browser SDK so you can experiment directly in DevTools.
{% /callout %}
Paste the snippet below into the browser console on any site to load the SDK from jsDelivr:
```js
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/@statsig/js-client@3/build/statsig-js-client+session-replay+web-analytics.min.js';
document.head.appendChild(script);
```
{% figure %}

{% /figure %}
{% /step %}
{% step title="Initialize and check the gate" %}
Replace YOUR\_SDK\_KEY with the client key from Step 4 and run:
```js
const client = new window.Statsig.StatsigClient('YOUR_SDK_KEY', {});
await client.initializeAsync();
```
Then call:
```js
client.checkGate('mobile_registration');
```
You should see false because the current session is not mobile and doesn’t use the employee email domain.
{% /step %}
{% step title="Simulate a mobile environment" %}
Enable the mobile device toolbar in Chrome DevTools.
{% figure %}

{% /figure %}
Re-evaluate the user to pick up the new environment and re-check the gate:
```js
await client.updateUserAsync({});
client.checkGate('mobile_registration');
```
The gate should now return true for the mobile profile.
{% figure %}

{% /figure %}
{% /step %}
{% step title="Test the employee backdoor" %}
Switch DevTools back to the desktop view and update the user with a company email:
```js
await client.updateUserAsync({ email: 'teammate@statsig.com' });
client.checkGate('mobile_registration');
```
The gate passes again because of the email rule.
{% figure %}

{% /figure %}
{% /step %}
{% step title="Flush exposures and inspect diagnostics" %}
```js
client.flush();
```
Open the gate’s Diagnostics tab to confirm each exposure, including the failing desktop check, mobile pass, and employee pass.
{% figure %}

{% /figure %}
{% /step %}
{% /steps %}
## Use the gate in production
Wrap feature logic in a gate check so only targeted users see the experience:
```js
if (client.checkGate('mobile_registration')) {
show(mobileRegistrationPage);
} else {
show(oldRegistrationPage);
}
```
Your feature gate is ready for production.
================================================================================
# When to Use Feature Gates vs. Experiments?
URL: https://docs.statsig.com/guides/featureflags-or-experiments
Updated: 2026-07-11
================================================================================
{% callout type="info" %}
In Statsig, feature flags are called feature gates. The terminology is interchangeable throughout this guide.
{% /callout %}
Both feature gates and experiments create control/test groups. Use this guide to pick the right tool for your launch and measurement goals.
***
## Quick Guidance
* **Choose a feature gate** when you want to roll out a feature gradually or monitor impact as you ramp.
* **Choose an experiment** when you need to compare multiple variants and quantify the lift across metrics.
***
## Key Differences
### Variants
* **Feature gate** → Two experiences only: pass vs. fail.
* **Experiment** → Any number of variants.
When viewing gate exposures you’ll see three buckets: Pass, Fail, and Fail – Not in Analysis. Statsig uses only the balanced subset of the fail group for metric comparisons. Learn more in the [gate exposure methodology](/feature-flags/view-exposures#gate-exposures).
### Return Values
* **Feature gate** → Boolean (`true`/`false`) so your application toggles code paths.
* **Experiment** → JSON config that describes the variant (colors, copy, thresholds, etc.).
### Ramping knobs
* **Feature gate** → Adjust Pass % to send more traffic to the new experience. You can go beyond 50/50 (e.g. 99% vs 1%).
* **Experiment** → Adjust Allocation % to enroll more users, but splits cap at 50/50.
After Statsig assigns a user, neither control reshuffles existing users. You can safely ramp without re-bucketing.
{% figure %}

{% /figure %}
***
## When Experiments Shine
Use experiments when you need:
1. **Multiple variants or personalization** – compare more than two options or tailor experiences using contextual bandits or layers.
2. **Stable identifiers and custom IDs** – analyze behavior before signup with stable IDs, or use custom IDs for sessions, workspaces, or geography.
3. **Isolated universes** – run parallel experiments safely by placing them in their own layers.
***
## When Feature Gates Shine
Feature gates are great for:
* **Safe rollouts** – gradually increase exposure while observing metrics.
* **Targeting audiences** – use gates as pre-filters before enrolling users in an experiment.
In experiment setups, gates often act as targeting criteria. The flow looks like this:
1. **Targeting gate** picks the eligible audience.
2. **Allocation %** (experiment) decides how much of that audience participates.
3. **Split %** distributes participants across variants.
After you choose a winner, lift the targeting gate and let the winning variant reach everyone.
***
## Summary: choosing the right tool
* Start with a **feature gate** if you have a single variant to launch carefully.
* Reach for **experiments** when you need quantitative comparisons across variants.
* Combine both when you want precise audience control plus rigorous measurement.
For more detail, refer to:
* [Feature gate exposures](/feature-flags/view-exposures)
* [Experiments overview](/experiments/overview)
* [Layers for mutual exclusion](/experiments/layers-overview)
================================================================================
# Create a Feature Gate
URL: https://docs.statsig.com/feature-flags/create
Updated: 2026-07-11
================================================================================
## In the Statsig console
### Create a new feature gate
1. Log into the Statsig console at https://console.statsig.com.
2. On the left-hand navigation panel, under **Feature Management**, select **Feature Gates**.
3. Click on the **Create** button.
4. Enter the name and description of the Feature Gate you want to create. Name your gate based on what you are rolling out, such as "Zippy Home Page" for a new homepage.
{% figure %}

{% /figure %}
5. Click **Create** to complete creating your Feature Gate.
6. You have created a new Feature Gate without any evaluation rules or conditions.
{% figure %}
{% /figure %}
### Add a rule to your feature gate
By default, a Feature Gate returns `false` when there are no rules configured. All users are blocked from seeing the feature until you set rules for who gets to "pass". The following steps walk through adding evaluation rules or conditions for a Feature Gate.
1. In Statsig console, under **Feature Management**, select **Feature Gates**.
2. Select the feature gate where you want to add a targeting rule
3. Click the **+ Add New Rule** button.
{% figure %}

{% /figure %}
4. Name your rule with something descriptive that other teammates will understand, such as "Mobile Users Only".
5. Configure your Feature Gate with the following options:
* **Environment(s)** - The [staging environment(s)](/guides/using-environments) you want your gate to apply to
* [**Criteria**](/feature-flags/conditions) - Specific evaluation conditions for the rule. Read more details about criteria and condition evaluation [here](/feature-flags/conditions).
* **Split %** - The percentage of users who meet the criteria that you want to pass or fail the gate check. The Fail % is automatically calculated based on the Pass % you set
* [**Overrides**](/feature-flags/overrides): A list of users you want to always bypass your gate (an allow list)
6. Statsig doesn't auto-save changes to Feature Gates and targeting rules. Select **Save** at the bottom right when you are ready for your changes to take effect.
## In your code
The previous steps covered setting up Feature Gates in the web console. For these gates to impact the behavior of your application, you also need to update your product code. The Statsig SDK handles the evaluation so that gate decisions take effect in real time.
### Initialize the Statsig SDK
If you have not already initialized the Statsig SDK, follow the [Installation Steps](/client/React#installation) in the language of your choice.
### Check a feature gate
Use the `checkGate` function in the Statsig SDK. You can find a code snippet for any particular gate by selecting the code snippet button on that gate's page in the Statsig console and selecting the SDK you want to use.
{% figure %}

{% /figure %}
Here's an example in [React](/client/React#basics-check-gate):
```tsx
const { client } = useStatsigClient();
return (
Gate is {client.checkGate("example_gate") ? "passing" : "failing"}.
);
```
In this example, the Statsig SDK checks from a client app whether any rules named "Example Gate" are set. The text "Gate is passing" is rendered for users who pass your gate conditions, and "Gate is failing" for users who fail the gate conditions.
{% callout type="tip" %}
Statsig offers over 20 client and server-side SDKs. Go to the full list of [SDKs](/sdks/quickstart#all-sdks) to find the one that best fits your needs.
{% /callout %}
## Common feature gate setups
### Kill switches
Set up a kill switch by setting an `Everyone` criteria's Pass percentage to 100%. To completely disable the feature for all users after code deployment, set the Pass percentage to 0%.
### OS-based flags
You can target users based on common application properties such as the operating system that the application is running on. For example, to target iOS users only, you can create a rule with the "Operating System" criteria, the "Any of" operator, and then listing "iOS" in the text field.
### Internal employees only
You can target users based on known user attributes. For example, you can use the user's **Email** attribute and the **Contains any of** operator, then enter the email domain of your company to target only internal employees.
### Defined user segments
You can also target users in a defined user [Segment](/segments/overview).
### Parent/child flags
You can target users based on their eligibility of other Feature Gates, enabling powerful chaining, hierarchy, and dependencies of flags. [Flag lifecycle management](/feature-flags/feature-flags-lifecycle) makes it easy to manage, deprecate, and maintain dependent flags.
================================================================================
# Feature Gate rule criteria
URL: https://docs.statsig.com/feature-flags/conditions
Updated: 2026-07-11
================================================================================
Statsig feature gates contain a list of rules that Statsig evaluates in order from top to bottom. This page describes how Statsig evaluates rules and lists all currently supported conditions.
## Rule evaluation
Statsig evaluates the rules you create in the order they're listed. For each rule, the **criteria** or **conditions** determine which users *qualify* for the Pass/Fail treatments. The Pass percentage further determines the percentage of *qualifying* users that Statsig exposes to the new feature. The remaining *qualifying* users see the feature disabled.
For rules set up as shown below, the following flowchart illustrates how Statsig evaluates them.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
After a user qualifies based on the condition in a given rule, Statsig doesn't evaluate subsequent rules for that user. Statsig then assigns the qualifying user to either the Pass or Fail group of that rule.
In the example, the third rule for **Remaining Folks** captures all users who don't qualify for the previous two rules. Removing this third rule means only users in pools 1 and 2 qualify for this feature gate and for analysis, not your total user base.
### Client vs server SDKs
All of the following conditions work on both client and server SDKs. Client SDKs handle several of these conditions automatically: if you don't provide a userID, client SDKs rely on an auto-generated stable identifier persisted to local storage. If you don't set an IP or User Agent (UA), the client SDK infers these attributes from the request. On mobile, the client SDK automatically passes your app version and locale to the server so Statsig can evaluate conditions using these attributes without you setting them explicitly.
### Stability
Evaluations at a given percentage are *stable* with respect to the unitID. For example, if the gate/config/experiment/layer has a unit type of "userID", and userID = 4 passes a condition at a 50% rollout, they will always pass at that 50% rollout. The same applies for `customIDs`, if the unit type of the entity is that `customID`. Want to reset that stability? Refer to "Resalting" below.
### Resalting
Gate evaluations are stable for a given gate, percentage rollout, and user ID. This stability is based on a salt associated with the feature gate. To reset a gate and reshuffle users, select "resalt" from the dropdown menu in the top right of the feature gate details page.
{% figure %}

{% /figure %}
### Partial rollouts
A 0% rollout means "off for all users matching this rule" and a 100% rollout means "on for all users matching this rule." Each rule lets you specify a percentage of qualifying users who pass (and see the new feature). Specifying a number between 0% and 100% creates a random allocation of users in Pass/Fail or test/control groups for a simple A/B test. Use this to validate that a new feature doesn't regress existing metrics as you roll it out. Statsig recommends a 2% → 10% → 50% → 100% rollout strategy. Each progressive rollout generates its own Pulse Results, as shown below.
{% figure %}

{% /figure %}
### User object fields
Evaluation uses the set of properties defined in the [StatsigUser object](/sdks/user). There are a set of reserved top-level fields, but these keywords are reserved and recognized in the `custom` and `privateAttributes` maps as well.
For example, if you set `user.country`, `user.custom.country`, or `user.privateAttributes.country`, Statsig uses that value to evaluate a country condition (checked in that order: top level, then custom, then privateAttributes, case-insensitively). If `user.country` isn't defined but `user.custom.COUNTRY` is, Statsig uses `user.custom.COUNTRY` to evaluate the country condition.
## Supported conditions
### User ID
Usage: Simple lists of User IDs to explicitly target or exclude from a gate.
Supported Operators: `Any of, none of`
Example usage: Add yourself (or a small group like your team) when you start building a new feature. Or exclude your designer until the feature is ready for review.
{% figure %}

{% /figure %}
### Email
Usage: Target based on the email of the user
Supported Operators: `any of, none of, contains any of, contains none of`
Example: Show new feature to people in the Statsig company with an authenticated @statsig.com email address
{% figure %}

{% /figure %}
### Everyone
Usage: Percentage rollout on the remainder of users that reach this condition. Think of it as "everybody else" - there could be a dozen other rules/conditions above it, but for everyone else, what percentage do you want to pass?
Supported Operators: `None. Percentage based only.`
Example usage: 50/50 rollout to A/B test a new feature. Or 0% to hide the feature for all people not matching a set of rules. Or 100% to show the feature to the remaining users who did not meet a condition above.
{% figure %}

{% /figure %}
### App Version
Usage: Users on a particular version of your app/website pass. Particularly useful for mobile app development, where a feature may not be fully ready (or may be broken) in a particular app version.
Supported Operators: `>=, >, <, <=, any of, none of`
Example: Turn off a feature for all users on app versions 3.0.0 through 3.1.0 as it was broken.
{% figure %}

{% /figure %}
### Browser Version
Usage: A particular version of a browser, parsed from the user agent. Should likely be combined with the browser name condition.
Supported Operators: `>=, >, <, <=, any of, none of`
Example: Turn off a feature for old versions of chrome which don't support a certain API
{% figure %}

{% /figure %}
### Browser Name
Usage: A particular browser, parsed from the user agent:
('Chrome',
'Chrome Mobile',
'Edge',
'Edge Mobile',
'IE',
'IE Mobile',
'Opera',
'Opera Mobile',
'Firefox',
'Firefox Mobile',
'Mobile Safari',
'Safari').
Often combined with the Browser Version condition.
Supported Operators: `>=, >, <, <=, any of, none of`
Example: Turn off a feature for old versions of chrome which don't support a certain API
To test: The Browser Name is inferred from the `userAgent`, but if you need to set it explicitly, you can set `browserName` in the user object.
{% figure %}

{% /figure %}
### OS Version
Usage: A particular os version the user is on, parsed from the user agent. Should likely be combined with the Operating System condition.
Supported Operators: `>=, >, <, <=, any of, none of`
Example: Turn off a feature for versions of macOS which don't support a certain API
{% figure %}

{% /figure %}
### Operating System
Usage: A particular operating system, parsed from the user agent:
('Android', 'iOS', 'Linux', 'Mac OS X', 'Windows').
Often combined with the OS Version condition.
Supported Operators: `any of, none of`
Example: Turn off a feature for versions of macOS which don't support a certain API
To test: The OS is inferred from the `userAgent`, but if you need to set it explicitly, you can set `deviceOS` in the user object.
{% figure %}

{% /figure %}
### Device Model
Usage: The device model of the mobile device the user is on.
Supported Operators: `any of, none of, is null, is not null, contains any of, contains none of, regex`
Example: Turn off a feature for older device models that your app doesn't support.
To test: The Device Model is automatically inferred by the SDK, but if you need to set it explicitly, you can set `deviceModel` in the user object.
{% figure %}

{% /figure %}
### Country
Usage: A 2 letter country code, either passed as the user's country or determined by the IP address.
Supported Operators: `any of, none of`
Example: Show a cookie consent banner for users accessing your service from the EU.
{% figure %}

{% /figure %}
### IP address
Usage: An IP address string
Supported Operators: `any of, none of`
Example: Show a different about us page on www.statsig.com to people in a certain IP address range
{% figure %}

{% /figure %}
### Passes Target gate
Usage: The condition passes if the referenced gate passes for the given user
Supported Operators: `gate_id`
Example: Only show feature X to people who also see feature Y: Only show a toggle to turn off ranking to people who also pass the new ranking algorithm gate.
{% figure %}

{% /figure %}
### Fails Target gate
Usage: Inverse of passes target gate: the condition passes if the referenced gate returns false for the given user
Supported Operators: `gate_id`
Example: Only show a new UI element to people who are not using the redesigned UI.
{% figure %}

{% /figure %}
### User in Segment
Usage: The condition passes if the user passes the rules defining the referenced segment. Refer to the [segments guide](/guides/using-environments) for more information on segments.
Supported Operators: `segment_id`
Example: Only show a new UI element to people who are not using the redesigned UI.
{% figure %}

{% /figure %}
### User not in Segment
Usage: The condition passes if the user fails the rules defining the referenced segment. Refer to the [segments guide](/guides/using-environments) for more information on segments.
Supported Operators: `segment_id`
Example: Only show a feature to people who are not in the premium/paid tier.
{% figure %}

{% /figure %}
### Environment Tier
Usage: The condition passes if the evaluation is happening in the given environment tier (development/staging/production). Refer to the [environments guide](/guides/using-environments) for more information on environments.
Supported Operators: `development/staging/production/(other)`
Example: Only show a feature on development builds
{% figure %}

{% /figure %}
### Custom Field
Usage: Specify the key in the custom object to fetch the value used on the left-hand side of a comparison.
Supported Operators:
* string: `any of, none of, contains any of, contains none of`
* number: `any of, none of, less than, greater than`
* version: `any of, none of, less than, greater than, less than or equal to, greater than or equal to`
* date: `before, after`
Example: Only show a feature to users who have turned on dark mode, as marked by the custom object having "darkmode": true.
{% figure %}

{% /figure %}
### Unit ID
Usage: Select custom ids to explicitly target or exclude from a gate.
Supported Operators: `any of, none of, is null, is not null, contains any of, contains none of, regex`
Example: Add yourself (or a small group like your team) when you start building a new feature.
{% figure %}

{% /figure %}
### Time
Usage: Evaluate a gate relative to the current time
Supported Operators: `after time, before time`
Value: time (displayed and input on Console in your browser's local time, but converted to a unix timestamp for evaluation)
Example: Show the labor day sale banner on labor day
{% figure %}

{% /figure %}
### Private attributes
Usage: Any user field or custom field condition can be used through a private attribute. To target an email without logging it, create an email condition and put `"email": "xyz@email.com"` in the `privateAttributes` dictionary. For custom fields, create a custom field condition and put the key/value pair in `privateAttributes` instead. Statsig uses `privateAttributes` for evaluation only and doesn't store or include them in log events.
Supported Operators: dependent on the condition
Example: Only show a feature to 20 somethings, as marked by the privateAttributes object having "age": "20-29".
{% figure %}

{% /figure %}
## FAQs
{% accordion-group %}
{% accordion title="Could users switch between control (fail) and test (pass) groups?" %}
After a user is exposed, they are included in the analysis going forward. They saw the new feature and were affected. If the feature gate rules are modified, or the user's attributes change so that the user no longer qualifies, they stop receiving the new feature. However, they continue to be counted for analysis. After you roll out the feature, all users see the new feature; if you turn off the feature gate, all users see the control (feature disabled). In either case, Statsig performs no further analysis.
{% /accordion %}
{% accordion title="How do overrides work? Are users included in overrides also included in the analysis?" %}
When you add user IDs in the **Pass** or **Fail** lists of your feature gate, these users see the appropriate treatment but aren't included in the analysis.
{% /accordion %}
{% /accordion-group %}
================================================================================
# Feature Gate overrides
URL: https://docs.statsig.com/feature-flags/overrides
Updated: 2026-07-11
================================================================================
## Override results of a feature gate
During development, you may need to explicitly state which users should pass or fail a given feature gate. Overrides let you do this.
Overrides are based on user IDs and you can set them to pass or fail for a given user ID. When Statsig evaluates a gate, if the user ID has an override, Statsig returns the overridden result immediately before evaluating any rules.
### Adding an override
- Log into the Statsig console at https://console.statsig.com
- On the left-hand navigation panel, select **Feature Gates**
- Select the feature gate where you want to add an Override
- Click the **Add Override** button
{% figure %}

{% /figure %}
- Select either 'Pass List' or 'Fail List' from the tabs in the dialog
{% figure %}

{% /figure %}
- For users you want to pass the gate, add them to the 'Pass List'
- For users you want to fail the gate, add them to the 'Fail List'
{% callout type="info" %}
A user can only exist on one of these lists at a time.
{% /callout %}
- After you have added the user IDs, select **Save**
### Deleting an override
If you add an override but later decide it isn't needed, you can remove it so Statsig evaluates the rules normally.
- Log into the Statsig console at https://console.statsig.com
- On the left-hand navigation panel, select **Feature Gates**
- Select the feature gate where you want to add an Override
- Click the **Edit Overrides** button
{% figure %}

{% /figure %}
- Select the trashcan icon next to the user ID you want to remove from a list.
{% figure %}

{% /figure %}
- After you have updated the lists, select **Save**
### Testing an override
After you add your override, you can test it in the **Test Gate** window by adding `userID` as a property of the user object.
{% figure %}

{% /figure %}
Users that pass see "PASS (User ID Override)"
{% figure %}

{% /figure %}
Users that fail see "FAIL (User ID Override)"
{% figure %}

{% /figure %}
================================================================================
# Test your Feature Gate
URL: https://docs.statsig.com/feature-flags/test-gate
Updated: 2026-07-11
================================================================================
There are three ways to test your feature gate and validate that it's working as expected with the rules you have created:
1. Using the built-in **Test Gate** tool in the Statsig console
2. Using the prototype Javascript **Test App** available in the Statsig console
3. Using the **Diagnostics** tab in the Statsig console
## Option 1: Use the Test Gate tool
To validate your feature gate using the built-in Test Gate tool:
* Log into the Statsig console at https://console.statsig.com
* On the left-hand navigation panel, select **Feature Gates**
* Select the feature gate that you want to validate
* At the bottom of the page, the **Test Gate** window lists all properties available in the rules you have created, as shown below:
{% figure %}

{% /figure %}
* Click in the window and edit the value of the Email property to include the users you want to target. For example, type jdoe@example.com as shown below. When the email domain matches "@example.com", the feature gate check succeeds and the window shows PASS. Otherwise, the gate fails and the window shows FAIL.
{% figure %}

{% /figure %}
## Option 2: Use the Statsig Test App
To validate your feature gate using the Test App:
* Log into the Statsig console at https://console.statsig.com
* On the left-hand navigation panel, select **Feature Gates**
* Select the feature gate that you want to validate
* At the bottom of the page, select **Check Gate in Test App** at the top right of the Test Gate window (shown by the red arrow). This opens a new browser window with a prototype JavaScript client that initializes and calls the Statsig `checkGate` API.
{% figure %}

{% /figure %}
## Option 3: Use the Diagnostics tab
To validate your feature gate using a live log stream:
* Log into the Statsig console at https://console.statsig.com
* On the left-hand navigation panel, select **Feature Gates**
* Select the feature gate that you want to validate
* Click on the **Diagnostics** tab (next to the Setup tab)
* Scroll down to the **Exposure Stream** panel, where you can see a live stream of gate check events as they happen:
{% figure %}

{% /figure %}
* In the **Event Count by Group** panel shown below, you can also validate that your application records events as expected for users who are exposed to the new feature (or not). If you have started recording a new event type to test the impact of a new feature, validate that these events appear as more users are exposed to the feature.
{% figure %}

{% /figure %}
================================================================================
# Viewing Feature Gate exposures
URL: https://docs.statsig.com/feature-flags/view-exposures
Updated: 2026-07-11
================================================================================
## Gate exposures
To see the number of users exposed to a feature gate:
* Log into the Statsig console at https://console.statsig.com
* On the left-hand navigation panel, select **Feature Gates**
* Select the feature gate that you want to test
* Click on the **Pulse Results** tab
* The **Cumulative Exposures** panel shows total exposures of a feature gate, broken down into three groups:
1. Units that passed the feature gate and were used for analysis.
2. Units that did not pass the feature gate and were used for analysis vs. the "Pass" group.
3. Units that did not pass the feature gate and were *not* used for analysis vs. the "Pass" group.
{% figure %}
{% /figure %}
## Balanced gates
Statsig balances gates and holdouts by default in Cloud, and as an opt-in option for Warehouse Native holdouts (on the Setup page). This reduces false positives at the cost of some sample size. This is a best practice, and Statsig defaults to a balanced analysis. Go to section 7 of [AB Testing Intuition Busters](https://drive.google.com/file/d/1oK2HpKKXeQLX6gQeQpfEaCGZtNr2kR76/view) for more discussion.
Statsig achieves this sampling during analysis by downsampling the larger arm proportionally to match the smaller group. For example, a `(100 - large_group_pct)/(large_group_pct)` sampling rate is applied to the larger group using an unbiased hashing approach, keeping the user pool consistent. Statsig rotates the hash salt across different gates and holdouts.
## Metric lifts
The **Metrics Lifts** panel shows how your feature is performing based on lifts in any business metrics added to the list of **Monitoring Metrics** for your gate.
Statsig uses an equal variant comparison (for example, 10% vs 10%) for calculating metric lifts for gate rollouts. Read more about the advantages of this methodology in ["A/B Testing Intuition Busters: Common Misunderstandings in Online Controlled Experiments"](https://www.researchgate.net/publication/361226478_AB_Testing_Intuition_Busters_Common_Misunderstandings_in_Online_Controlled_Experiments) by Ron Kohavi, Alex Deng, & Lukas Vermeer.
In the example below, the rises in *product view count* and *purchase event count* are statistically significant, suggesting this feature positively impacts the number of product views, but may actually be negatively impacting conversions to purchases.
{% figure %}
{% /figure %}
## Bots & filtering
{% callout type="info" %}
Information on Bots & Filtering has been moved to its [own page](/experiments/monitoring/bots)
{% /callout %}
================================================================================
# Pre-Post Results
URL: https://docs.statsig.com/feature-flags/pre-post-results
Updated: 2026-07-11
================================================================================
{% callout type="info" %}
**Cloud only feature**
This feature is available only on Statsig Cloud. As Statsig works on a WHN solution, reach out to Statsig if you're interested in being an early customer.
{% /callout %}
## How Pre-Post Results work in Statsig
Pre-Post Results is an analysis mode for Feature Gates that lets you estimate the impact of feature rollouts when traditional A/B testing isn't possible. By comparing key metrics before and after a feature gate is rolled out from 0% to 100% of users, you can identify the directional impact of your features in production.
{% figure %}

{% /figure %}
Pre-Post is particularly valuable for:
* **Emergency rollouts** - Features that need to ship immediately without time for a slow rollout
* **Infrastructure changes** - Backend improvements or technical features that affect all units/pods/users and can't be partially rolled out
* **Retroactive analysis** - Understanding the rough impact of features that were already rolled out without experiments
* **Regulatory or ethical features** - Changes that can't be withheld from a control group
## Pre-Post Results aren't experiments
Pre-Post analysis measures the change in a metric around a specific point in time, among a specific set of exposed units. It doesn't meet the requirements of a proper A/B test or feature gate partial rollout. Pre-Post analyses are best understood as snapshot measurements around the time you launched your feature. Because many other factors can occur at the same time, there's no guarantee that your results are due to your feature launch. Correlation doesn't equal causation.
Experiments remain the standard for measuring feature impact. Run launches as an Experiment or partial Feature Gate rollout when accuracy, validity, and extensibility are important.
## When does Statsig calculate Pre-Post Results?
Statsig calculates Pre-Post Results for targeting rules that meet specific rollout conditions:
1. The targeting rule started at 100% pass rate or was rolled out from 0% to 100% in a single step
2. The rollout happened in the last 30 days
When you select a qualifying rule in the Metrics Impact tab, Statsig automatically switches to Pre-Post Results mode and displays a banner indicating you're viewing Pre-Post analysis.
## How Pre-Post Results calculates feature impact
Pre-Post Results uses the following approach to estimate feature impact:
1. **Identify the participating units** - Find all users who were exposed to the feature after the 100% rollout
2. **Collect pre/post-rollout data** - Gather metric values for these users from the periods before and after the rule change
3. **Bucket metric data into discrete periods** - Statsig automatically groups metric data into buckets of a consistent duration.
4. **Calculate the difference** - Compute the mean metric values for both pre and post periods, treating each bucket as a unique observation, then calculate the delta (difference) between them
This method ensures the same users are compared before and after the feature rollout.
## Supported metric types
| Metric type | Supported |
|-------------|-----------|
| Event Count | ✅ Yes |
| Event Count Custom | ✅ Yes |
| Event User | ✅ Yes |
| Sum | ✅ Yes |
| Mean | ✅ Yes |
| Funnel | ❌ No |
| Ratio | ❌ No |
| Participation Rate | ❌ No |
## Best practices
When using Pre-Post Results, follow these guidelines:
* Focus on metrics that are directly related to your feature's intended impact and have sufficient volume. The more directly a metric responds to the feature launch, the easier it is to detect a sudden change.
* Correlation doesn't equal causation. Consider other changes, seasonal effects, or external events that might influence your metrics during the analysis period.
* Use Pre-Post Results as one data point alongside qualitative feedback, user research, and business context to make informed decisions.
* If you can partially roll out a feature to less than 100% of users, do so. This lets you measure the metric impact for users seeing the feature compared to users not seeing the feature, and arrive at true causation.
## Known limitations
* **30-day window** - Only rollouts from the last 30 days are supported
* **No control group** - Results show correlation, not definitive causation
* **External factors** - Other changes during the analysis period can influence results
* **Metric type restrictions** - Some advanced metric types aren't yet supported.
================================================================================
# Measuring multiple rollout stages
URL: https://docs.statsig.com/feature-flags/multiple-rollout-stages
Updated: 2026-07-11
================================================================================
## Continuous analysis
For gate rules that roll out with a pass percentage ≤ 50% and without any rollback, Statsig consolidates data collected at earlier and later stages of the rollout into one analysis. This produces a more complete analysis.
Valid continuous analysis rollouts include:
10% → 20%
10% → 20% → 50%
Invalid continuous analysis rollouts include:
10% → 5% (rollback)
10% → 70% (exceeds 50%)
When a rollout is no longer valid for continuous analysis, Statsig analyzes the new rollout step separately from previous steps.
## Compatibility with other gate features
### Balanced gates
Gate rules that have multiple rollout stages are also [balanced](/feature-flags/view-exposures#balanced-gates) using downsampling.
In cases where pass percentage ≤ 50%, Statsig uses the same hashing as the pass/fail and takes an equal percentage of the other group.
When pass percentage > 50%, Statsig uses an orthogonal random hashing to sample the larger group at a rate of $\frac\{1-p}\{p}$ where p is the fraction of users in the larger group. In the case of a gradual rollout, this prevents bias based on enrollment time period.
### CUPED/CURE
CUPED/CURE is available only when there have been no rollbacks and when the rollout percentage hasn't exceeded 50% within a gate rule. Analysis is no longer continuous after a rollback or after 50% is exceeded, because earlier treatments bias the pre-exposure data of a given unit.
================================================================================
# Scheduled Rollouts
URL: https://docs.statsig.com/feature-flags/scheduled-rollouts
Updated: 2026-07-11
================================================================================
Feature Gates provide a safe, controlled feature rollout. Scheduled Rollouts add a time-based scheduling layer to Feature Gates, letting you pre-set any rollout schedule you want and execute it automatically. This is useful when a feature launch happens in another timezone or when your team follows a standard ramp-up schedule with every feature release.
Scheduled rollouts apply at the Feature Gate **rule** level for maximum flexibility. Not all rules in your Gate need to include a scheduled rollout. Set it up only for the rules that need it.
## Set up a Scheduled Rollout
To set up a Scheduled Rollout on a rule in your Feature Gate, tap the "…" in the upper right-hand corner of the rule you want to schedule a rollout for.
{% figure %}

{% /figure %}
Select **Edit Rule or Rollout**, and then select **Schedule Automated Rollout**.
{% figure %}

{% /figure %}
From here, configure each phase of your Scheduled Rollout. The upper right-hand corner shows your current pass percentage, which reflects the baseline pass percentage you entered for your rule and which you can change using **Edit Rule**. To add phases to your rollout, select **Add Phase** and configure as many phases as you need.
Each scheduled rollout phase includes:
* Rollout date
* Rollout time\*
* Pass percentage
{% callout type="note" %}
Rollout times are available in 15 minute increments. Additionally, each configured phase represents a discrete increase to the next rollout percentage, not a gradual rollout amortized over the course of the entire phase.
{% /callout %}
{% figure %}

{% /figure %}
As you build your Scheduled Rollout, a preview of the phases appears below the configuration wizard. This preview is also available to viewers of your Feature Gate when they hover over a rule.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
## Execute a Scheduled Rollout
After configuration, each phase of a Scheduled Rollout executes automatically on the specified schedule. Statsig sends Scheduled Rollout notifications to the Feature Gate creator, any editors, and anyone following the Feature Gate.
Statsig sends notifications through:
* Email
* Console
* (Optional) Slack: to configure Slack notifications, go to *Account Settings > Notifications*.
================================================================================
# Safeguards
URL: https://docs.statsig.com/feature-flags/safeguards-overview
Updated: 2026-07-11
================================================================================
Safeguards continuously monitor critical metrics and automatically intervene in your Feature Gate rollouts when risk thresholds are exceeded. This ensures faster recovery from issues, eliminates the need for constant manual checks, and protects your users from unintended impact.
{% callout type="info" %}
Safeguards is available on the Pro and Enterprise billing tiers.
{% /callout %}
## When to use Safeguards
Use Safeguards when you want to:
* Limit the impact of a feature on critical business or performance metrics
* Automate rollout progression to more users based on how your metrics are performing
* Maintain system stability by automatically responding to API errors, latency spikes, or infrastructure issues
## How Safeguards work
Safeguards listen to alert types that you have created in your project, such as Rollout Alerts and Topline Alerts. When any alert fires due to metric regressions, Safeguards automatically pause your rollout, roll it back, or finish a rollout based on your settings.
**Pre-requisite:** You must create at least one Rollout Alert or Topline Alert before configuring a Safeguard on a Feature Gate. Go to the [Alerts](/product-analytics/alerts-overview) documentation for setup instructions.
## Two types of safeguards
A Safeguard can use two types of alerts to take an action on your Feature Gate:
| Alert type | Definition | Use case |
|---|---|---|
| Rollout Alert | Monitors the regression of metric delta between users who pass and fail your Feature Gate | Use when you want to ensure your Feature Gate isn't causing negative drift for users receiving the new flag variation. Works only on partially rolled out rules (pass rate between 0% and 100%). |
| Topline Alert | Monitors absolute metric values regardless of Feature Gate assignment | Use when you want to take action on your Feature Gate when system metrics breach thresholds, regardless of confirming causation. Works on fully rolled out (0% or 100%) and partially rolled out rules. |
## Get started with Safeguards
Follow these tutorials to start using Safeguards:
* [Create a new Safeguard](/feature-flags/safeguards-create)
* [Manage an existing Safeguard](/feature-flags/safeguards-manage)
================================================================================
# Create a Safeguard
URL: https://docs.statsig.com/feature-flags/safeguards-create
Updated: 2026-07-11
================================================================================
To create a Safeguard on a rule, follow these steps:
1. Go to a Feature Gate's **Setup** tab
2. Pick a targeting rule for which you want to monitor regressions
3. Click the three-dot (...) menu and choose **'Safeguard Rule'**
{% figure %}

{% /figure %}
4. Choose the action to take when alerts fire:
- **Rollback to 0%** - Assigns default value to all users.
- **Roll out to 100%** - Assigns Pass value to all users.
- **Pause Rollout** - Stops scheduled rollout progression (only available with an active Scheduled Rollout policy).
{% figure %}

{% /figure %}
5. (Optional) Set how long to monitor alerts for safeguarding the rule:
| Alert type | Evaluation period | Evaluation start time |
| ------------- | ----------------- | ----------------------------------------------- |
| Topline alert | Choose your own | Starts when the safeguard is created |
| Rollout alert | Fixed (90 days) | Starts whenever targeting rule's pass % changes |
{% figure %}

{% /figure %}
{% callout type="tip" %}
**Recommended:** Monitor topline alerts for crashes, errors, latency, and similar issues _for a few days_ after a Feature Gate rollout to confirm stability. A 14-day evaluation period is a good starting point.
{% /callout %}
6. Select one or more alerts to monitor:
- Rollout alerts - For feature-specific regression detection
- Topline alerts - For system-wide health monitoring
{% figure %}

{% /figure %}
7. Click **Save**
================================================================================
# Manage a Safeguard
URL: https://docs.statsig.com/feature-flags/safeguards-manage
Updated: 2026-07-11
================================================================================
## View a Safeguard
To view an existing Safeguard, tap the blue pill on your Feature Gate's targeting rule. You can see how your Safeguard is defined and make changes. You can add or remove alerts, change the action, or adjust the evaluation period.
{% figure %}

{% /figure %}
## When a Safeguard triggers
When a safeguard is triggered because of an alert:
* The configured action executes automatically (rollback/pause/complete).
* A banner appears on the targeting rule with the action taken, timestamp, and diagnostic link.
* Further rule modifications are blocked until the alert is resolved.
* Statsig sends notifications per your alert configuration.
{% figure %}

{% /figure %}
================================================================================
# Managing Feature Gate lifecycles
URL: https://docs.statsig.com/feature-flags/feature-flags-lifecycle
Updated: 2026-07-11
================================================================================
A feature can go through different phases throughout its lifecycle: it may be tested by a few users, recently rolled out to everyone, or stable enough that the toggle is no longer needed.
A feature gate should clearly reflect the phase the feature is in, for a few important reasons:
* **Prevent incidents**: Avoid scenarios where old code for a deprecated feature is accidentally touched or repurposed. This has had real business consequences, as in [how Knight Capital lost half a billion dollars](https://www.statsig.com/blog/lose-half-a-billion-dollars-with-bad-feature-flags-knight-capital).
* **Maintain a healthy codebase**: Dead references to flags increase the volume of code your team navigates daily and can slow down new developer onboarding.
* **Reduce mental load**: The feature gate catalog shows the status of every feature, so you can see at a glance what next steps are needed (for example, launch or end a feature) without tracking everything manually.
## Managing feature gate lifecycles
Statsig makes it easy for your feature gates to reflect the phase your feature is in by using **status**. A gate can be in one of four statuses:
{% figure %}

{% /figure %}
| Status | What it represents | Implication |
|-------------|-----------------------|---------|
| In Progress | This feature is being rolled out and tested. | Default status when you create a gate. |
| Launched | This feature has been rolled out to everyone. | The gate always returns **TRUE** and stops generating billable exposure events, so you stop incurring costs. The gate reference is likely safe to clean up from the codebase. |
| Disabled | This feature has been rolled back from everyone. | The gate always returns **FALSE** and stops generating billable exposure events, so you stop incurring costs. The gate reference is likely safe to clean up from the codebase. |
| Archived | This feature is no longer referenced in code or checked; history on the gate is preserved. | The gate has received 0 checks for the last 7 days and will receive no more checks. |
## When to update feature gate lifecycles
There are 3 points throughout the gate's lifecycle when you should take action, either in the console or in your codebase:
{% figure %}

{% /figure %}
**1) The gate has been fully rolled out or rolled back, and you are ready to skip rule evaluation and assign a default value**
* Go to the feature gate page and click the "..." menu in the upper right corner to select **Launch** or **Disable**:
* **In Progress → Launch**: when the gate has been rolled out to 100% for a period of time (recommended: more than 30 days) and you are comfortable with the gate always returning TRUE.
* **In Progress → Disable**: when the gate has been rolled back to 0% for a period of time (recommended: more than 30 days) and you are comfortable with the gate always returning FALSE.
* To find all gates that are good candidates to be **Launched** or **Disabled** (rolled out to 100% or rolled back to 0% more than 30 days ago):
* Go to Feature Gates catalog
* Click on filter icon:
* Status = In Progress
* Pass Rate = 100% AND 0%
* In the search bar: "Modified: `", {});
```
Now, fetch the config and construct the banner:
```js
const bannerConfig = client.getDynamicConfig("banner_config");
const text = bannerConfig.get("text", null);
const backgroundColor = bannerConfig.get("backgroundColor", "black");
const color = bannerConfig.get("color", "white");
const fontSize = bannerConfig.get("fontSize", 14);
if (text == null) {
return;
}
const banner = document.getElementById("homepageBanner");
const bannerText = document.createElement("p");
banner.style.display = "block";
bannerText.innerHTML = text;
banner.style.color = color;
banner.style.fontSize = fontSize + "px";
banner.style.backgroundColor = backgroundColor;
banner.appendChild(bannerText);
```
This JavaScript relies on the HTML page having a homepageBanner div:
```html
```
With a small amount of JavaScript, this example integrates with the Statsig SDK and uses the Dynamic Config. You can now add a new rule to the Dynamic Config and return a different banner to a different group of users without updating the website.
Here is an example of the result when viewing the webpage in Chrome on a Mac:
{% figure %}

{% /figure %}
This example shows one approach to using Dynamic Config with your app, website, or backend service.
================================================================================
# Segments
URL: https://docs.statsig.com/segments/overview
Updated: 2026-07-11
================================================================================
## What is a segment?
A segment defines a reusable set of users. You can define a segment based on common user attributes such as location, client device, browser, client application version, or user IDs.
## When to use segments
Segments are ideal for targeting a commonly identified set of users across features or dynamic configs. For example, you can create a segment representing your team members or company employees to test a new feature before launching to external users. You can also create different segments for English-speaking and Spanish-speaking countries to deliver a localized experience to each segment.
To get started, go to the Statsig guide to [create your first segment](/guides/first-segment).
The following tutorials show how to perform common tasks with segments.
* [Create a segment](/segments/create-new)
* [Create rules for a segment](/segments/add-rule)
* [Use a segment](/segments/implement)
================================================================================
# Create a segment
URL: https://docs.statsig.com/segments/create-new
Updated: 2026-07-11
================================================================================
To create a segment:
* Log in to the Statsig console at https://console.statsig.com
* On the left-hand navigation panel, select **Segments**
* Click the **Create** button
* Enter a name and description for your segment
* Select the type of segment you want to create: **Conditional** or **ID List**, and click **Create** (Conditional is selected by default)
{% figure %}

{% /figure %}
For **Conditional Segments**, you can **[Add Rules](/segments/add-rule)** to complete the segment's definition.
For **ID List Segments**, you can **[Add an ID List](/segments/add-id-list)** to complete the segment's definition.
================================================================================
# Adding Rules
URL: https://docs.statsig.com/segments/add-rule
Updated: 2026-07-11
================================================================================
## Create a rule for a segment
A rule defines the criteria for which users are included in a segment. To add a rule to a segment:
* Log in to the Statsig console at https://console.statsig.com
* On the left-hand navigation panel, select **Segments**
* Select the segment where you want to add a rule
* Click the **Add New Rule** button
* Select the criteria to target a set of users. For example, select **Email** with the **Contains any of** operator, then enter your company's email domain to target only internal employees.
{% figure %}

{% /figure %}
* Click the **Add Rule** button
* Click the **Save Changes** button at the top right of the **Rules** section
================================================================================
# Adding ID Lists
URL: https://docs.statsig.com/segments/add-id-list
Updated: 2026-07-11
================================================================================
## Adding ID lists
### What is an ID list?
An ID List enables you to define a reusable audience segment using user identifiers like `userID`, `stableID`, or `organizationID`.
You can manage ID Lists by manually adding or removing IDs, uploading CSVs, or replacing the entire list.
### Creating an ID list segment
1. Navigate to the **Segments** section in the [Statsig Console](https://console.statsig.com).
2. Click **Create New Segment**.
3. Toggle the segment type from **Conditional** to **ID List**.
{% figure %}

{% /figure %}
4. Select the **ID type** you want to build from.
{% figure %}

{% /figure %}
### Managing IDs
Once inside your ID List segment, you have a variety of options:
* **Manual Entry**: Enter IDs directly into the input box.
{% figure %}

{% /figure %}
* **Upload CSV**: Import a list of IDs from a CSV.
* **Bulk Actions**: Choose how your input affects the existing list:
* **Add**: Add IDs to the list
* **Remove**: Remove matching IDs from the list
* **Replace**: Clear all IDs and replace with a new list
You can also sync an ID list segment from sources such as [Amplitude Cohorts](https://help.amplitude.com/hc/en-us/articles/4789303290011), [Segment Audiences](/integrations/data-connectors/segment#syncing-statsig-segment-id-lists-with-segment-personas-audiences), or a custom source using the [Console API](/console-api/introduction).
There's a hard limit of 10 million IDs across all ID Lists in your project.
### Keep ID lists small
ID Lists with fewer than 1,000 IDs are synchronized in the same process as feature gates, experiments, and conditional segments, providing high consistency and reliability. ID Lists over 1,000 IDs use a separate synchronization process for scalability, which means they aren't included in the main initialization path (for server SDKs or the local evaluation version of client SDKs) and change propagation takes longer.
Keep each ID List to no more than 1,000 IDs unless the longer propagation time is acceptable.
### Large ID lists
To target hundreds of thousands or millions of users, the more performant approach is to set an attribute on the User object passed to the Statsig SDK. For example, pass `user_type:Paid` or `user_type:Trial` in the User object instead of maintaining large ID Lists. This requires your app to have this information available when calling the Statsig SDK. If your use case requires ID lists larger than 10,000 IDs, contact the Statsig team through Slack.
{% callout type="info" %}
Large ID Lists is currently in limited Early Access. Contact Statsig Support to enable. Note this is not available for Free/Pro users at this time.
{% /callout %}
================================================================================
# Using a segment
URL: https://docs.statsig.com/segments/implement
Updated: 2026-07-11
================================================================================
You can use a segment to target a set of users in a feature gate or a dynamic config:
* Select the feature gate or dynamic config where you want to target users
* Click the **Add New Rule** button
* In the criteria, select the **User in Segment** option
* Select the segment that you want to use and enter a name
{% figure %}

{% /figure %}
* Click the **Add Rule** button
* Click the **Save Changes** button at the top right of the **Rules** section
================================================================================
# Release Pipeline Overview
URL: https://docs.statsig.com/release-pipeline/overview
Updated: 2026-07-11
================================================================================
Release Pipelines enable multi-stage rollout strategies that respect your infrastructure boundaries, providing greater control over feature deployments.
## When to use Release Pipelines
Consider using Release Pipelines when you need to:
* Implement infrastructure-aware deployment strategies beyond what traditional feature flags offer
* Safely roll out changes in complex, distributed systems with minimal risk
* Deploy progressively across environments (dev → staging → prod)
* Target specific infrastructure segments with precision (e.g., prod-us-west → prod-us-east → prod-eu)
* Control progression between stages with time intervals or manual approvals
* Monitor each deployment stage before proceeding to the next
* Enable instant rollbacks if issues arise during any phase
**Especially valuable for:**
* Platform engineering teams managing multi-environment, multi-region infrastructure
* DevOps practitioners implementing progressive delivery strategies
* SREs responsible for maintaining system reliability across complex deployments
* Organizations with mission-critical services that can't afford downtime
## Key concepts
| Concept | Description |
|---------|-------------|
| **Release Pipeline** | Defines the complete release strategy for a feature gate or dynamic config change |
| **Phase** | A distinct stage in the pipeline with specific conditions targeting designated release segments |
| **Trigger** | An event that starts when a feature gate or dynamic config begins using a Release Pipeline |
| **Action** | Controls you can apply to an active Trigger to manage the release process |
## Get started with release pipelines
{% callout type="note" %}
Release Pipeline is currently only supported in Statsig's [Server Core SDKs](https://www.statsig.com/blog/introducing-statsig-server-core-v0-1-0). Legacy SDKs continue to work but won't get the full features of release pipelines.
{% /callout %}
Follow these tutorials to begin implementing Release Pipelines:
* [Create and Manage Release Pipelines](/release-pipeline/create-and-manage)
* [Trigger a Release Pipeline](/release-pipeline/trigger)
* [Manage an Ongoing Release Pipeline](/release-pipeline/actions)
## Current limitations
* Experiments aren’t supported in release pipeline
* Resalt/Disable/Delete/Archive/Launch actions won’t trigger a release pipeline
* [Scheduled rollouts](/feature-flags/scheduled-rollouts) aren’t supported to work in conjunction with release pipeline
================================================================================
# Create and Manage Release Pipelines
URL: https://docs.statsig.com/release-pipeline/create-and-manage
Updated: 2026-07-11
================================================================================
## Creating a new pipeline
To create a new Release Pipeline:
1. Log into the [Statsig console](https://console.statsig.com)
2. Navigate to **Settings** > **Feature Management**
3. Under Release Pipelines, click the **Create** button
4. Enter a descriptive name for your pipeline
5. Click **Create** to proceed to the configuration page
{% figure %}

{% /figure %}
## Configuring phases
Each pipeline consists of one or more phases, with each phase representing a distinct release target.
### Adding phases
For each phase in your pipeline:
1. Add one or more release rules
2. Select a required **Environment** for the rule
3. Optionally add custom field conditions for more precise targeting
{% figure %}

{% /figure %}
### Setting phase transitions
Control how your phases progress with these transition options:
| Transition Type | Description |
|-----------------|-------------|
| **Require Review** | Requires manual approval from an authorized user before starting the phase |
| **Time Interval** | Automatically proceeds to the next phase after a specified duration (in minutes) |
:::note
You can combine both options in a single phase. When both are used, the time interval only begins counting down after someone gives the required approval.
:::
{% figure %}

{% /figure %}
## Managing existing pipelines
### Updating a pipeline
To modify an existing pipeline:
1. Click on the pipeline name from the list
2. Make your edits to any section
3. Click **Save** to apply your changes
**Important:** You can't modify pipelines with active rollouts in progress until those rollouts complete or you stop them.
### Viewing pipeline references
There are two ways to see which feature gates and dynamic configs are currently using a pipeline:
1. Project Settings
* Navigate to Project Settings
* Click on the Feature Management menu in the left-rail
* Navigate to Release Pipelines section
* Click on the **References** column against each Release Pipeline
* This shows all feature gates and dynamic configs that are currently attached to a specific pipeline
{% figure %}

{% /figure %}
2. Feature Gates / Dynamic Configs Page
* Navigate to Feature Gates / Dynamic Config list view
* Filter by 'Release Pipeline' current status
* This shows all feature gates and dynamic configs with an ongoing release pipeline
{% figure %}

{% /figure %}
## Opting out environments from Release Pipelines
By default, all environments trigger Release Pipelines when changes are made. You can configure specific environments to be exempt from this behavior.
When you opt an environment out from Release Pipelines:
* Changes made exclusively to that environment don't trigger a Release Pipeline.
* This allows quick environment-specific adjustments without starting the full release process.
### How to opt out an environment
To exclude an environment from triggering Release Pipelines:
1. Navigate to **Settings** in the Statsig console
2. Under **Keys & Environments**, select **Environments**
3. Click on the environment you wish to opt out
4. Unselect the **Pipeline-required Environment** option
5. Click **Save** to apply your changes
{% figure %}

{% /figure %}
================================================================================
# Trigger a Release Pipeline
URL: https://docs.statsig.com/release-pipeline/trigger
Updated: 2026-07-11
================================================================================
Statsig activates a Release Pipeline when you make changes to a feature gate or dynamic config that has a pipeline attached to it.
## Attaching a pipeline
Before triggering a release, attach a pipeline to your feature gate or dynamic config. The Statsig console offers two methods for attaching a Release Pipeline.
### During feature creation
You can select a Release Pipeline directly in the creation modal when setting up a new feature gate or dynamic config:
{% figure %}

{% /figure %}
### To an existing feature
For an existing feature gate or dynamic config, you can attach a Release Pipeline through the sidebar settings:
{% figure %}

{% /figure %}
:::note
You must have at least one Release Pipeline created before it appears in the dropdown menu. Refer to [Create and Manage Pipelines](/release-pipeline/create-and-manage) for instructions on creating pipelines.
:::
## Starting a rollout
When a Release Pipeline is attached, making changes to your feature gate or dynamic config automatically starts the pipeline process:
1. Make your changes to the feature gate or dynamic config
2. Click **Save** to commit the changes
3. A confirmation dialog appears, informing you that changes progress through the pipeline
4. Review the information and click **Confirm** to proceed
The system then begins the rollout following the phases defined in the attached pipeline.
{% figure %}

{% /figure %}
## Viewing release status
After you trigger a Release Pipeline, you can monitor its progress:
1. At the top of the feature gate or dynamic config page, a status banner appears
2. This banner displays the current phase and overall progress through the pipeline
{% figure %}

{% /figure %}
For information about controlling an ongoing release, including approvals and cancellations, refer to [Managing Release Actions](/release-pipeline/actions).
## Frequently asked questions
**Q: Can I attach different Release Pipelines to different feature gates?**\
A: Yes, each feature gate or dynamic config can use a different pipeline based on its specific rollout needs. However, a single feature gate or dynamic config can only have one Release Pipeline attached at a time.
**Q: What happens if I need to cancel a release in progress?**\
A: You can stop an ongoing release using the actions menu in the release details view. Refer to [Managing Release Actions](/release-pipeline/actions) for complete instructions.
================================================================================
# Manage an Ongoing Release Pipeline
URL: https://docs.statsig.com/release-pipeline/actions
Updated: 2026-07-11
================================================================================
## Managing release actions
After you trigger a Release Pipeline, you can control its progression using the following actions. These controls let you safely test, pause, fast-track, or halt propagation of feature gate and config changes across pipeline phases.
{% figure %}

{% /figure %}
### Approve
**What it does:**
Start a phase that requires manual approval before rollout begins. Use this when your workflow requires human verification before changes move forward.
**How to use it:**
1. The pipeline automatically pauses at the last completed phase awaiting approval
2. You receive a notification prompting you to take action
3. Go to the Release Pipeline status page
4. Click the ⋯ menu and select 'Approve'
5. Confirm the action. The next phase begins rolling out.
### Pause
**What it does:**
Stops the bake timer between two phases. Useful if you want to delay rolling out the next phase to investigate the current phase.
:::note
Pause doesn't stop the current phase. It stops only the timer to move to the next phase.
:::
**How to use it:**
1. Go to the Release Pipeline status page
2. Click the ⋯ menu and select 'Pause'
3. Confirm to pause the bake timer
### Unpause
**What it does:**
Resumes a previously paused bake timer, allowing the pipeline to move to the next phase after the remaining wait time.
**How to use it:**
1. Go to the Release Pipeline status page
2. Click the ⋯ menu and select 'Unpause'
3. Confirm to resume the bake timer
### Skip
**What it does:**
Immediately skips the current phase and moves rollout to the next defined phase. Useful for fast-tracking safe changes.
**How to use it:**
1. Go to the Release Pipeline status page
2. Click the ⋯ menu and select 'Skip'
3. Confirm to resume the bake timer
### Abort
**What it does:**
Halts the release process. The feature gate or dynamic config reverts to its pre-release state, and no further changes propagate.
**Important:** Aborting a release is irreversible. After aborting, you must trigger a new release to restart the process with any modifications.
**How to use it:**
1. Go to the Release Pipeline status page
2. Click the ⋯ menu and select 'Abort'
3. Confirm to resume the bake timer
### Full roll out
**What it does:**
Skips all intermediate phases and releases the latest version across all environments and custom attributes once.
**How to use it:**
1. Go to the Release Pipeline status page
2. Click the ⋯ menu and select 'Full Roll Out'
3. Confirm to resume the bake timer
================================================================================
# Product Analytics Overview
URL: https://docs.statsig.com/product-analytics/overview
Updated: 2026-07-11
================================================================================
## Metrics Explorer
Metrics Explorer provides data-driven insights that can inspire new features. After you implement these features, you can release and measure their impact using Statsig feature gates, A/B tests, or experiments to understand how they affect core metrics.
### Charts in Metrics Explorer
In Metrics Explorer, charts are the primary means of analysis, providing a detailed look at your data. You can switch between chart types from the top-left section of the interface while maintaining context on the selected events. This flexibility lets you focus on a particular metric or event while exploring it from different angles.
Statsig offers the following chart types:
* [Metric Drilldown](/product-analytics/drilldown)
* [Funnels](/product-analytics/funnels)
* [Retention](/product-analytics/retention)
* [Distribution](/product-analytics/distribution)
* [User Journeys](/product-analytics/user-journeys)
* [Lifecycle](/product-analytics/lifecycle)
Get started with Metrics Explorer charts by navigating to the **Analytics** section of the Statsig console navigation menu and selecting [Metrics Explorer](https://console.statsig.com/metrics/explore).
## Dashboards
[Dashboards](/product-analytics/dashboards) let you share insights and information with your team. Any chart you build can be saved to a dashboard. You can also save a snapshot of Feature Gates, A/B tests, and experiments to dashboards. Experiment and feature gate snapshots sync automatically to a dashboard, keeping your team up to date with relevant launches as they emerge. These dashboards are auto-populated with any entity matching the tags you specify.
### Charts in Dashboards
You can add any chart supported in Metrics Drilldown to dashboards. Dashboard charts aren't static. After expanding a chart using the \[ ] icon, you can modify it for further exploration without saving the change to the dashboard. To save changes, click the edit icon or button and save.
To get started, navigate to **Dashboards** in the Statsig left navigation.
================================================================================
# Metric Drilldown Charts
URL: https://docs.statsig.com/product-analytics/drilldown
Updated: 2026-07-11
================================================================================
The Metric Drilldown chart in Metrics Explorer is a tool for understanding customer behavior and trends within your product. Use it to analyze key metrics and user behavior over time. You can also filter to specific properties or cohorts and use Group-By to compare behaviors between groups.
## Use cases
* **Trend Analysis Over Time**: Track how specific metrics evolve over time. Visualizing product data in Metrics Explorer lets you compare key performance indicators and user behavior, and understand long-term trends and short-term fluctuations.
* **Identify Cohorts**: Define and explore cohorts by filtering to users who performed certain events at frequencies you define.
* **Understand Feature and Experiment Impact**: Split any metric by Experiment Group or Feature Gate Group to compare metric performance across groups. Use automatically generated chart annotations for feature launches and experiment starts to correlate decisions with changing trends.
* **Segmentation and Comparison**: Analyze how different user segments or product features perform to identify areas providing value or areas that need improvement.
* **Filtering**: Focus on specific segments or cohorts for more targeted analysis.
* **Statistical Understanding**: Understand how the average, median, or other percentile value (for example, p99, p95) of a metric changes over time.
* **Dynamic Metric Creation with Formulas**: Create new metrics dynamically using custom formulas to derive ad-hoc insights.
* **Flexible Visualization Options**: Choose from line charts, bar charts, horizontal bar charts, stacked bar charts, and other formats to best represent your data.
* **Event Samples for Debugging**: Access a metric's underlying sample events and the user-level information attached to each event. Useful for troubleshooting trends or anomalies.
* **Detailed Data Control**: Adjust data granularity from high-level overviews to detailed breakdowns. Use rolling averages to smooth data for trend analysis.
* **Debug Experiments**: Break down your experiment's first exposures to understand how properties or groups (feature gates, experiments, holdouts) affect the experiment.
* **View Sample Ratio Mismatch (SRM)**: See the SRM of your experiments over time and drill down into event and user metadata to understand how properties (country, browser) or groups affect experiment SRM.
* **Debug Feature Gates**: Break down feature gate first exposures per rule to understand how properties or groups affect the gate.
* **Analyze Dimensions Across Metrics**: Identify top or bottom performers across multiple metrics by viewing data in a table and sorting by any metric-column combination.
## Using the metric drilldown chart
## Selecting metrics and events
In Metrics Explorer you can choose events, custom-metrics, auto-generated metrics, or experiment exposures to explore. You can add several metrics and events or exposures to plot on a single chart.
### Events
When selecting an event, the total number of times the event occurred (Count) for a given data point (hour, day, etc.) is plotted by default. You can also choose different aggregation methods:
* **Count**: Plot the number of times the event occurred within the given time range per data point.
* **Unique**: Plot the number of unique ids (generally UserIDs) that performed the event in the given time range per data point.
{% figure %}

{% /figure %}
* When viewing data on uniques (e.g. unique users) at daily granularity, you can choose to have the value of each daily data point represent the number of unique weekly users (unique users over previous 7 days). This enables you to get a sense of how weekly usage is changing day over day.
{% figure %}

{% /figure %}
* **Average**: Plot the average of a selected event property value per data point. This works only for properties with numerical values.
{% figure %}

{% /figure %}
* **Sum**: Plot the sum of a selected event property value per data point. This works only for properties with numerical values.
{% figure %}

{% /figure %}
* **Percentiles**: Plot the value of a selected event property at the selected percentile per data point.
{% figure %}

{% /figure %}
* **Unique Values**: Plot the count of distinct values for any property across events or users within the given time range per data point. This aggregation helps answer questions like "How many different referrers drove traffic last week?" or "How many SKUs were added to carts today?" by counting unique property values rather than event occurrences.
* **Count per User**: Plot the frequency distribution of how often users perform a specific event, showing statistics like average, median, or percentile values per user within the given time range per data point. This aggregation helps analyze user engagement patterns by measuring how many times each user performed an event, then applying summary statistics across those users.
### Exposures
Selecting an experiment or gate exposure plots its first exposures over your selected date-range. First exposures are the first time a unique id (set on the experiment or gate) was exposed to each of your experiment groups or each of your gate's rules.
### Understanding first exposures in feature gates
When a gate is checked for a user, an exposure is created for the rule whose conditions the user met. If a user is exposed to multiple rules at different times, the first exposure from each rule is kept. Group by rule to see each rule's exposures separately.
### Metrics
Selecting a custom Metric or auto-generated Metric plots the value of that metric over your selected date-range.
**Viewing and modifying the metric definition**
The metric definition appears directly below the metric name. You can also make ad-hoc edits to the event-based definition to plot new metrics dynamically, based on existing metric definitions.
### Comparing multiple metrics, events, or exposures
You can compare multiple metrics or events by plotting them on the same chart. To add a metric, click the **+** icon and select **Metric**, then select the metric.
When multiple metrics are plotted, click any metric's row in the table below the chart to scope to that metric. Click the row again to show all metrics.
Use the checkboxes next to metric names in the table to scope to a custom set of metrics.
### Filtering
Filters let you scope to events and metrics logged by users with specific properties.
To add a filter to an event or metric, click the filter icon and select the event or user property to filter by.
### Adding formulas
Formulas let you dynamically combine and transform plotted events and metrics using mathematical expressions to answer new questions.
To add a formula, hover over the **+** icon in the Metrics section and select **Formula**. A free-form text field appears. Use the label of each plotted metric or event as a variable (each plotted metric is labeled with a letter). For example, if you have:
* Metric A: number of unique purchasers
* Metric B: total purchases
You can plot purchases per purchaser as `B/A`.
## Drilling down
In addition to plotting metrics, you can drill into your metrics to identify unique groups that reveal how people use your product.
### Group-By
A Group-By disaggregates plotted metrics and events by a selected property or group, letting you compare how an action or user behavior correlates with a specific property. Adding a Group-By splits the plotted metrics into separate plots. By default, the top ten groups by value are shown on the chart. You can select up to 50 groups when the chart is set to daily granularity.
A metric can be grouped-by event properties, user profile properties, experiment group, or feature gate group.
To adjust Group-By limits, add a group-by, go to the summary table below the charts, and click the **Top X series** dropdown. Select how many groups to display (up to 50). This feature is available for line charts, stacked-line charts, bar charts, and stacked-bar charts.
{% figure %}

{% /figure %}
When you have a Group-By applied, you can view the results as raw numbers, or as a percentage.
**Feature gate and experiment groups**
You can also select a Feature Gate or Experiment to split a metric by the different groups in the selected test.
**Adding a group-by**
To add a group-by, hover over the **+** icon in the *Drilldown* section and select **Group By**. Then select the property or experiment group to split the metric by.
**Quickly hiding or isolating lines**
When using Group By, you can isolate lines to show data for specific groups. Click the row representing a group in the table view below the chart. Clicking an already-isolated group shows all groups again.
Use the checkboxes in the table view to scope to a custom set of groups.
### Define and compare event-based cohorts
Building a useful group of users to analyze often requires more precision than comparing by property values. For example, you may want to understand the behavior of power users and compare them against your general user base.
To support this, you can define event-based cohorts. Select an event of interest, then define a frequency criterion for how often users in the cohort performed the event:
* Performed the event ***at least*** some number of times
* Performed the event ***at most*** some number of times
* Performed the event ***exactly*** some number of times
You can also define the time window in which a user performed the event, and filter to a specific property for more granular cohort definitions.
You can save cohorts to reuse them later.
## Exploring a metric drilldown chart
### Selecting chart granularity
The Metric Drilldown chart supports daily, hourly, 30-minute, 5-minute, and 1-minute granularity. The granularity setting controls the interval between x-axis values. Granularities below hourly limit the analysis time window to 1 day.
The default granularity is daily. To change it, select the **Daily** dropdown in the top right of the chart.
When viewing data on uniques (for example, unique users) at daily granularity, you can set each daily data point to represent the number of unique weekly users (unique users over the previous 7 days). This shows how weekly usage is changing day over day.
### Setting the date range
The default date range is 14 days. To change it, click the **Last 14 days** dropdown and select a quick date range or a custom range.
### Changing your unit type
The default unit type is numbers. To view data as a percentage, time value, or measurement of space (bytes, bits, etc.), click the settings cog in the top right corner of the chart and select a format unit type.
### Smoothing out the data with rollups
Metrics like daily usage often have seasonality effects that make longer-term trends harder to see. Rollups such as a 7-day rolling average help smooth this data.
Statsig supports the following rollups to smooth out data, each of which can be rolled up over 3, 7, 14, 28, 48, or 60 data points:
* **Rolling average**: Replaces each data point with the average of the preceding selected number of data points.
* **Rolling sum**: Replaces each data point with the sum of the preceding selected number of data points.
* **Cumulative Sum**: Replaces each data point with the sum of all preceding data points, including itself. The result is a continuously increasing total where each value represents the accumulated sum.
### Selecting the chart visualization
Metrics Drilldown offers many ways to visualize your data, including:
* **Line:** Useful when plotting one or more metrics over time.
* **Stacked Line:** Useful when comparing groups to understand the relative proportion a certain group has of a metric or event.
* **Bar:** Useful when comparing the total value of two metrics over the entire date range.
* **Horizontal Bar:** Ideal for grouped data comparisons, especially when you have longer label names. Makes it easy to identify top and bottom performers across any business dimension like user types, locations, or platforms.
* **Donut:** Useful for visualizing the proportional breakdown of a whole into distinct categories at a single point in time. Perfect for showing how different segments (like countries, user types, or feature groups) contribute to your total metric value. Apply a Group-By to any metric to see the breakdown as a donut chart.
* **World Map:** Visualize your metrics geographically by country when using a country-based Group-By. This view makes it easy to spot geographic trends and understand how your product performs across different regions.
* **Single Value:** Display key metrics at a glance for quick summaries. Perfect when you need to highlight a single important number or KPI without the complexity of a full chart.
* **Data Table:** Compare multiple metrics across groups in a structured table format. Ideal for detailed analysis when you need to examine exact values and perform side-by-side comparisons of different segments.
Both donut charts and world maps work with any metric when you apply a Group-By. Select your metric, add a Group-By for the property you want to analyze (such as country for geographic analysis), and choose your preferred visualization from the chart type selector.
### Zooming in
To zoom in on a portion of the date range, click at one end of the range, hold the mouse button down, and drag to the other end. Release the mouse button to zoom in. To reset the zoom, click **Reset Zoom** in the top right of the chart.
### Sharing your insights
When you want to share an insight, you have several options:
* **Share by URL:** Copy the URL. This shares the query as currently defined.
* **Create a share link:** Click the **…** button in the top right of the chart and select **Share Link**. This copies a shortened URL to the clipboard.
* **Share to Dashboard:** Click the **…** button and select **Export to Dashboard** to save the chart to an existing dashboard or create a new one.
Sharing a chart through a URL or shortened link captures a snapshot of the chart as defined when the link was copied. Subsequent changes aren't captured in the share link.
================================================================================
# Funnel Charts
URL: https://docs.statsig.com/product-analytics/funnels
Updated: 2026-07-11
================================================================================
## How funnel charts work
Funnel Charts in Metrics Explorer show what portion of users complete each step of a journey you define through your product or service. These charts are useful for understanding user behavior, identifying bottlenecks, and developing insight-driven product changes that help users convert more successfully.
### Example use cases
* **Conversion Analysis**: Monitor the progression of users through stages like sign-up, adding to cart, and purchase completion.
* **Identifying Drop-off Points**: Pinpoint where users drop off, so you can target improvements at those steps.
* **Comparing User Segments**: Observe how different user segments move through the funnel, highlighting variations in behavior based on demographics, user types, or other criteria.
* **Product Optimization**: Determine which features or steps effectively move users to the next stage, and which require improvements.
* **Experiment Analysis**: Understand the conversion rates before and after first exposure to an experiment.
## Defining a user funnel
### Step 1: Add steps to your funnel
To define a funnel, select a series of events that represent different parts of a product flow you want to understand.
To do so:
1. Go to **Metrics Explorer** under **Analytics** in the Navigation Bar, and switch over to the Funnel Charts view.
2. Add steps to your funnel using the **+** icon. Optionally add filters to funnel steps to target specific event or user properties. For WHN, add steps directly from your **Metric Source**. For Statsig Cloud, use events or metrics for the steps.
**Combining multiple events into one step**
You can combine multiple events into a single step. This is useful when more than one qualifying event represents a single meaningful portion of your funnel. Combined events use OR logic. To do this:
1. On the step, click the **…** button and select **Combine Events**.
2. Select an additional event to add.
3. Add any filters to each event in the funnel step.
Repeat as needed. You can combine up to 5 events per step.
{% figure %}

{% /figure %}
**Filtering to the first time a user performed an event**
Filtering to the first-time experience is useful because first-time behavior can affect long-term retention and may differ significantly from general product flows. You can filter funnel events to the first time a user (or other unit ID) ever performed an event:
1. Click **…** next to the event.
2. Select **Filter to First Time**.
{% figure %}

{% /figure %}
**Renaming steps**
When sharing a Funnel Chart (for example, by saving it to a dashboard), logged event names may not be easily readable. You can rename funnel steps for legibility:
1. Click **…** next to the event name.
2. Click **Rename Funnel Step**.
3. Enter a readable name for the step.
Renaming a step doesn't change the underlying event name. The rename applies only to the funnel being configured.
### Step 2: Select a graph type
Three graph types are supported for understanding conversion funnels:
* **Conversion Rate**: A standard funnel view showing a step-by-step breakdown of where users drop off and how many convert through each step.
* **Conversion Rate Over Time**: Shows how the overall conversion rate has changed over time. Useful for understanding how features and product changes affected the funnel conversion rate.
* **Time to Convert**: Shows the distribution of time it takes users to complete the funnel.
### Step 3: Choose an ID type
In general, construct funnels using a userID to understand the rate at which individual users complete each step.
Some funnels may involve multiple people in an organization. For example, you may want to understand end-to-end company onboarding where different people handle purchasing, deploying, and using the service.
Statsig supports individual or group analytics, letting you analyze funnel success at the user level or for whole groups such as organizations or companies.
You can choose any ID type defined in your Statsig project when creating a funnel.
If an experiment exposure is selected, make sure the ID type matches that of the experiment.
{% callout type="info" %}
Group analytics isn't a paid add-on at Statsig and is included at no extra cost for all tiers.
{% /callout %}
### Step 4: Define the conversion window
After you define your funnel, you can limit the analysis to users (or other unit IDs) who converted within a specified time frame. Users who start the funnel but don't convert within this time frame are counted as dropped off.
### Step 5: Drilldown
To understand how conversion varies between user groups, use the **Group By** feature to split funnels by properties, experiment groups, or feature flag groups. Click **+** to the right of **Group By** and select the property, experiment, or feature flag to split the funnel conversion analysis by.
## Advanced funnel analysis
### Ordered or unordered funnels
Ordered funnels are the default and the most common scenario. They require a user to complete the selected events in the specified order to be counted as converted. The user may perform other events between the specified events and still be counted as converted. For example, for an ordered funnel with events A, B, C, and D, the sequence A→B→B→A→C→D counts as converted.
To check only whether a user completed all events regardless of order, toggle **Ordered** off in the advanced settings. Unordered funnels require only that the user completes the specified events within the given time range.
### Unique users (or unit ID) vs total conversions
You can define funnel analysis by the total number of conversions or the number of unique users who convert. The default is Unique Users.
### Daily aggregation
When enabled, this calculates funnel conversions per calendar day. A unit with funnel conversions on multiple days is counted as multiple conversions. This setting is enabled by default.
## Interpreting your user funnel
### Conversion rate vs. number of conversions
At the top right of the funnel chart, the **Conversion Rate vs. Conversions** selector lets you switch the y-axis between conversion rate and number of conversions. When used with a Group By, toggling between the two views shows both relative and absolute conversion scales across different user groups.
### Conversion summary and table
Under each funnel step, a summary shows:
* The percentage and number of units that converted, relative to the first step
* The percentage and number of units that dropped off, relative to the first step
* The average time for each user to convert
The conversion table shows the percentage and number of units that converted relative to both the first step and the previous step. This is useful when grouping by a property to compare conversions between user groups.
{% figure %}

{% /figure %}
### User exploration
By clicking any bar in the funnel, you can download all users in that segment who dropped off or converted as a CSV file. You can also view session streams to get an event-by-event understanding of a user's experience before and after that point in the funnel.
{% figure %}

{% /figure %}
## Conversion Drivers
Conversion Drivers identify statistically significant factors that correlate with funnel conversion or drop-off at each step. The analysis surfaces event properties, user properties, and intermediary events that influence user progression through the funnel.
{% figure %}

{% /figure %}
To access Conversion Drivers:
1. Click on any funnel step
2. Select **"View Drop-Off & Conversion Drivers"**
3. Configure the analysis scope using the dropdown filters
{% figure %}

{% /figure %}
### Analysis configuration
You can configure which data types to include in the analysis:
* **Event Properties**: Attributes attached to events (e.g., platform, plan\_type, referral\_code)
* **User Properties**: User-level attributes (e.g., country, account\_age, signup\_method)
* **Intermediary Events**: Events that occurred between the selected funnel steps
### Driver metrics
Each identified driver displays:
* **Conversion Likelihood**: Expressed as a multiplier of the funnel's conversion rate (e.g., users with platform::Android are 1.2x as likely to convert)
* **Conversion Rate**: Percentage of users with this factor who converted
* **Participant Share**: Percentage of total funnel participants who had this factor
### Detailed analysis
Click on any driver to access the drilldown view, which provides:
* **Conversion Matrix**: Side-by-side comparison of conversion outcomes for users with and without the factor
* **Correlation Coefficient**: Statistical measure of the factor's association with funnel completion (also known as the [phi coefficient](https://en.wikipedia.org/wiki/Phi_coefficient))
You can group the funnel by any identified driver by clicking the group by option. This reconfigures the funnel chart to show conversion performance segmented by the selected property.
### Use cases
Conversion Drivers are useful for:
* Exploratory analysis when investigating funnel performance without predefined hypotheses
* Root cause analysis of conversions and drop-offs
* Validating assumptions about user segment behavior
* Monitoring funnel performance changes over time
{% callout type="note" %}
Conversion Drivers require a Pro plan subscription or Enterprise plan with the Advanced Analytics package.
{% /callout %}
================================================================================
# Retention Chart
URL: https://docs.statsig.com/product-analytics/retention
Updated: 2026-07-11
================================================================================
## How Retention charts work
Retention charts in Metrics Explorer help you understand how effectively your product or service maintains user interest and engagement over time. Retention charts are useful for measuring product-market fit and are critical for overall product growth. By analyzing retention patterns, you can identify whether your product or certain features resonate with your audience, and which areas need improvement.
{% figure %}

{% /figure %}
### Use cases
* **Long-term user engagement tracking:** Monitor retention over days, weeks, or months to understand how long users stay engaged after their initial interaction or a specific trigger event.
* **Stickiness of specific features:** Identify which product features users return for.
* **Sub-population analysis:** Filter your retention analysis to specific groups to compare engagement across different user segments.
## Creating a retention chart
You define a retention chart with a start event, a return event, and a return window. This lets you answer the question: "For users who performed the starting event, what percentage performed the return event within the given conversion window, over successive days or weeks?"
### Step 1: Define a start event
The start event (or trigger event) indicates that a user has started using the product or service. Depending on the question you want to answer, this is often a top-of-funnel event such as sign-up, any active event, or an event that represents use of the feature whose retention you want to understand.
To analyze the retention of a specific group of users, optionally add a filter to scope your analysis.
### Step 2: Define a return event
The return event indicates that the user is getting value from your service. Depending on the question you want to answer, this can be returning to your product (another active event) or use of a specific feature.
You can also add a filter to the selected event based on user or event properties.
### Step 3: Choose an ID type
Statsig supports individual and group analytics. This lets you analyze the retention of individual users or of whole groups such as organizations or companies.
### Step 4: Choose a return time window
Choose whether to measure daily or weekly retention. When users are not expected to engage daily to be considered "active", a weekly return window is more useful. A weekly window answers: "For users who perform the start event, how many perform the return event within 1 week?"
Daily retention defines each cohort as users who performed the start event on a given calendar day. The chart then shows, over your selected time range (for example, 30 days), what percent of users in the cohort returned on each day.
Weekly retention defines each cohort as users who performed the start event within a calendar week. Each cohort spans successive weeks. The chart then shows, over your selected time range, what percent of users in the cohort returned during each week.
### Step 5 (Optional): Choose a chart granularity for weekly retention charts
When you select weekly retention, you can optionally change the chart granularity by selecting "Daily" from the granularity dropdown in the top left of the chart.
Granularity is distinct from the Return Time Window. When you create a weekly retention chart with daily granularity, the chart shows, on a given day, what percent of the cohort performed the return event within the last week. For example, day 12 indicates what percent of the cohort performed the return event during the 7 days spanning day 6 through day 12, inclusive.
## Understanding a retention chart
[Retention charts](https://www.youtube.com/watch?v=mqlHpYimik8) in Statsig include two main sections: the **retention graph (j-curve)** and the **retention table (triangle chart)**.
### Retention table (triangle chart)
A retention table, or triangle chart, shows how well you retain users after their first interaction with your product or service. It tracks cohorts of users based on their first engagement date (defined by your start event) and shows the percentage of those users returning (defined by your return event) over subsequent days or weeks.
The leftmost column identifies each cohort. Across the top are time intervals (days or weeks) after the initial engagement. Each cell shows the proportion of users from a cohort who were active at a specific time interval.
A vertical analysis (looking down a column) lets you compare retention rates of different cohorts at the same lifecycle stage. A horizontal analysis (reading across a row) shows how a single cohort's retention evolves over time. High numbers in the first few columns suggest strong initial engagement; consistent percentages over longer intervals indicate successful long-term retention.
Columns start at day/week 0, the earliest day/week in the time range. The rightmost column shows retention for the most recent day/week.
Column behavior depends on the selected granularity:
- **Daily retention:** Each column shows the percentage of the cohort that performed the return event on that calendar day.
- **Weekly retention with weekly granularity:** Each column shows the percentage that performed the return event within that week.
- **Weekly retention with daily granularity:** Each column shows the percentage that performed the return event on that calendar day or within the 6 days prior.
### Retention graph
A retention graph plots the percentage of retained users against time. The X-axis represents time, segmented into daily or weekly intervals since the users' first interaction with the product. The Y-axis shows the percentage of the original users who remain active (performed the return event).
**Reading the graph**
The retention graph illustrates the rate at which users disengage over time.
* A **declining curve** followed by a flattening is common. This indicates a drop in user interest after the first interaction, followed by stabilization among a core user group. The higher the percentage at which the curve flattens, the better the retention health of the product.
* A **smiling curve** shows an initial decline in retention followed by an upward trend at later stages. This pattern often results from successful re-engagement strategies or product improvements that bring back previously inactive users.
* A **continuously declining curve** indicates a consistent loss of users over time, often pointing to issues in user satisfaction or engagement.
Key aspects to observe:
1. **Initial Drop-off Rate:** The steepness of the initial decline indicates how many users stop using the service after their first experience.
2. **Long-term Engagement:** The slope in the later stages of the graph shows the long-term user engagement. A flatter slope at this stage means better user retention.
3. **Trends Over Time:** Comparing multiple graphs over different time periods can reveal the impact of product changes or external factors on retention.
## Scoping to specific cohorts
By default, Statsig plots only the "All cohorts" retention curve. To scope to a single cohort, click anywhere on that cohort's row in the table. To compare multiple cohorts, use the checkboxes on the leftmost portion of the table to select the cohorts whose retention curves you want to compare.
================================================================================
# Distribution
URL: https://docs.statsig.com/product-analytics/distribution
Updated: 2026-07-11
================================================================================
## How distribution charts work
Distribution charts in Metric Explorer help you visualize the range of user experiences across your product. These charts are useful for understanding central tendencies, evaluating product health, and identifying outlying behavior.
{% figure %}

{% /figure %}
### Use cases
* **Analyze the spectrum of experiences:** Study the distribution of event values to identify any trends.
* **Measure feature engagement:** Visualize how often each user is interacting with a critical event.
* **Monitor product performance:** Ensure that your product isn't experiencing performance issues like unusual latency times.
## Creating a distribution chart
### Step 1: Choose a metric or event
The first step is to decide whether to use a metric or an event. The distribution of a metric shows the aggregated property value per unit ID. For example, if the metric aggregation is by count, the chart displays the number of times each user triggered the event. The distribution of an event shows the range of data stored under the **Value** property for each logged event. Statsig displays the event distribution only when the data under **Value** is numeric.
### Step 2: Refine your bucket size
By default, the bucket size is 1, with a minimum value of 0 and a maximum value of 10. Buckets are always lower-inclusive and upper-exclusive. If the range exceeds the maximum bucket value, data is shown as "10+". You can adjust bucket sizes, minimums, and maximums to find the best view of your data.
## Interpreting your distribution chart
### Distribution of a metric
For the distribution of a metric, the chart always represents metric values per unit ID. The X-axis represents the metric value, which can be a count of events or the sum or average of a property value, depending on the metric configuration. The Y-axis represents the unit ID, which can be users, stable IDs, or a custom ID.
### Distribution of an event
The distribution of an event always displays the range of data under the **Value** property. The X-axis shows the **Value** and the Y-axis shows the number of events.
================================================================================
# User Journeys
URL: https://docs.statsig.com/product-analytics/user-journeys
Updated: 2026-07-11
================================================================================
## How User Journeys works
The User Journeys chart shows the many paths users are taking through your product so you can better understand the end-user experience.
{% figure %}

{% /figure %}
### Use cases
* **Event pathways:** Follow the most common paths users take through your product.
* **Identifying drop-off points:** Pinpoint where users drop off within key product flows.
* **Discovering unexpected behavior:** Uncover surprising usages and iterate on your product.
## Creating a user journeys chart
### Step 1: Choose a starting or ending event
Choose whether to explore a journey that starts or ends with a specific event. Starting events are often top-of-funnel events such as sign-up or log-in, but can also be an event trigger for any new feature you introduce into your product. Ending events let you understand where users came from before reaching a specific point.
You can optionally add filters to scope your analysis to a specific group.
### Step 2: Choose an ID type
Statsig supports individual and group analytics. This lets you analyze the paths of registered users with User IDs, Stable IDs, or your own custom IDs.
### Step 3: Define your measurement criteria
Under the "Measured as" section, choose between total conversions or unique conversions. Under total conversions, users can re-enter the flow each time they trigger the start event. Under unique conversions, each user appears in the path only once.
You can also specify the observation duration. This defaults to 1 hour, but you can change the length of time users have to convert to the next event. Adjusting this value helps validate whether users are passing through flows at the pace you expect.
To add more context, break the User Journey out by an event property. Select a specific event property to view the user journey expanded by its different property values (similar to a group-by). For example, you can view user journeys split by operating system.
You can also filter out user paths that include a specified event.
After editing the measurement criteria, click **Run Query** to apply your selections.
### Step 4: Adjust your chart visualization
At the top bar of the chart, you can edit the conversion percent threshold, hide events, and expand your time window. The conversion percent threshold sets the minimum percentage of conversions needed to be considered relevant. Events below the threshold are consolidated under "Others". The **Hide Events** selector lets you filter out events you don't want to see. The time selector lets you view user journeys during the time window you care about most.
## Using User Journeys
After selecting your start event, a sankey diagram shows all the next steps users took. Click any node to display the next events users triggered. Each event shows the total number of events counted and the percentage of users who triggered it. Continue clicking nodes to explore user flows through your product.
================================================================================
# Lifecycle
URL: https://docs.statsig.com/product-analytics/lifecycle
Updated: 2026-07-11
================================================================================
## How Lifecycle charts work
Lifecycle charts in Metrics Explorer help you understand how usage changes over time by classifying your unique units (for example user\_id) within each time interval based on whether they used an event recently, returned after a gap, continued to use the event, or churned.
{% figure %}

{% /figure %}
### Use cases
* **Track new-user ramp after a launch:** See whether adoption is growing week over week after shipping a new feature.
* **Monitor churn and reactivation:** Understand whether users are falling off and whether they return.
* **Compare retention health across releases:** Spot changes in retention and reactivation patterns over time.
## Creating a lifecycle chart
### Step 1: Choose an event (or a compatible metric)
Decide whether to use a metric or an event. Lifecycle is designed for a single underlying event and count-style metrics composed of a single count-type event.
### Step 2: Choose your unit (unique units)
Select what you want to count uniquely (for example user\_id, stable\_id, or another unit). The chart reports how many unique units fall into each category for each time interval.
### Step 3: Choose your interval (granularity)
Pick an interval unit (day / week / month) and a number of intervals per bucket (1–48). Each bar on the chart represents one interval bucket, and the chart shows one data point per bucket across the selected date range (max 1 year).
{% figure %}

{% /figure %}
### Step 4: (Optional) Add filters
Apply filters to focus on a specific segment (for example platform, country, app version, or a feature-related property).
## Interpreting your lifecycle chart
Each interval bucket classifies unique units into four categories (mutually exclusive):
**New:** Used during this interval and didn't use at any point earlier within the lookback window (up to 1 year before this interval).
**Resurrected:** Used during this interval, didn't use in the immediately previous interval, and used earlier within the lookback window.
**Recurring:** Used during this interval and the immediately previous interval.
**Dormant:** Used in the immediately previous interval, but not during this interval (Statsig displays this below the axis to make churn visually apparent).
### Reading the chart
**X-axis:** Time, grouped into your chosen interval buckets.
**Y-axis:** Count of unique units.
**Stacked bars:** Show how the composition of usage changes over time (new vs. resurrected vs. recurring), while the dormant component highlights drop-off between adjacent intervals.
================================================================================
# Dashboards
URL: https://docs.statsig.com/product-analytics/dashboards
Updated: 2026-07-11
================================================================================
## Creating a dashboard
There are two ways to create a dashboard:
1. Navigate to the *Dashboards* tab and click **Create**. You can choose one of Statsig's [Dashboard Templates](#dashboard-templates) or create a custom dashboard.
{% figure %}

{% /figure %}
2. You can also create a dashboard directly from Metrics Explorer. To do this, after you finish building a chart:
1. Click **Export to Dashboard** at the top right corner of the chart.
2. Name the chart.
3. Select **Create New Dashboard** from the Dashboard Destination selector.
4. Give your new dashboard a name.
{% figure %}

{% /figure %}
### Adding charts, feature gates, and experiments to a dashboard
Dashboards help teams share and review product insights. You can add Metric Charts and track ongoing Pulse results from A/B tests and feature launches.
There are several types of dashboard widgets you can add or create including:
* **Charts:** Create a new chart directly from a dashboard or export a chart created in Metrics Explorer to a dashboard. Supported charts include:
* Drilldown Charts
* Funnel Charts
* Retention Charts
* Distribution Charts
* User Journey Charts
* **Text**: Annotate dashboards with context or create section headers for better readability.
* **Single Value:** Highlight a hero metric with clarity by adding a single value to the dashboard.
* **Experiment, Feature Flag:** Get a quick snapshot of an experiment or feature flag.
* **Funnel Metrics:** Visualize custom funnel metrics.
### To add a widget to a dashboard
1. Click **Add Widget**.
2. Select the type of widget you want to add.
3. Configure the widget, for example select a chart type and then select events and metrics you want to track.
4. Save the widget to the dashboard.
## Exploring your dashboard
### Edit date ranges
By default, Statsig syncs charts and widgets on a dashboard with the date range set for the entire dashboard. To update this default date range, click the pencil icon in the top right corner. In the settings panel, you can modify the dashboard's title, description, and **Default Date Lookback Range**. Statsig applies this range automatically each time you open the dashboard. Choosing **Chart Default** allows each chart to use the date range originally set when it was first added to the dashboard.
{% figure %}

{% /figure %}
You can also change the date range dynamically by modifying the date picker at the top right of the dashboard. Changes to this selector synchronize all charts and widgets on the dashboard. Statsig doesn't save these changes when you reopen the dashboard.
{% figure %}

{% /figure %}
### Exporting your dashboard
To share a static version, print, or save your dashboard, click the settings dropdown (**...**) in the top right corner and select **Export as PDF**. Statsig generates a PDF of the dashboard and downloads it automatically within a few seconds.
{% figure %}
{% /figure %}
### Cloning your dashboard
To duplicate a dashboard, open it, click the settings dropdown (**...**) in the top right corner, and select **Clone**. A dialog appears to confirm the clone. After cloning, Statsig takes you to the new dashboard.
{% figure %}

{% /figure %}
### Filtering your dashboard
Select the filters button below the dashboard name to add a global filter. The filter applies across all eligible widgets, so you can view updated results without filtering each widget individually. You can also use free-form text to apply generic filters, such as filtering emails that contain `@gmail.com`.
When you expand a dashboard widget that has a dashboard filter applied, the filter appears as an **Inherited Dashboard filter**. Any changes you make at the widget level are temporary and Statsig doesn't save them.
{% figure %}

{% /figure %}
#### Default dashboard filters
Default dashboard filters let you pin commonly used filters directly to your dashboards. These filters appear at the top of the dashboard and apply across all eligible widgets, enabling quick comparisons across dimensions like company, region, or platform, without rebuilding filters from scratch.
To configure default dashboard filters:
1. Navigate to your dashboard and click the settings cog ⚙️
2. Scroll to **Default Filters** and configure the filters you want to pin to the dashboard.
3. Click **Save**.
After configuration, the pinned filters appear at the top of the dashboard. You can swap filter values to see how different users, cohorts, or properties affect the same set of charts. When you change a filter value, all charts update automatically. This allows you to reuse the same dashboard with dynamic filtering instead of duplicating dashboards or manually editing each widget's filters.
### Refreshing your dashboard widgets
To ensure your dashboard data is up to date, you can refresh dashboard widgets in several ways:
**Manual Refresh**: Click the refresh button shown below to refresh all dashboard widgets at once.
{% figure %}

{% /figure %}
**Automatic dashboard refreshes**: Statsig can automatically refresh dashboards on a schedule. Results are cached for faster loading.
You can configure a refresh frequency for each dashboard (for example, hourly or daily). Queries for that dashboard run on the specified schedule and store the results. When someone opens the dashboard, the most recent cached data appears immediately without triggering fresh queries.
To configure automatic dashboard refreshes:
1. Navigate to your dashboard and click the settings cog.
2. Scroll to **Schedule Dashboard Refresh** and set the interval.
3. Click **Save**.
This feature is useful for shared dashboards or recurring check-ins where fresh data should be ready without manual effort.
### Dashboard subscriptions
Dashboard subscriptions send a scheduled snapshot of a dashboard to your team so stakeholders can stay up to date without opening Statsig.
**What gets delivered:**
A subscription delivers a snapshot of the dashboard at send time, including:
* Dashboard title and link
* All widgets currently on the dashboard
* The dashboard’s default date range (and any pinned default dashboard filters, if configured)
Depending on your workspace setup, Statsig can deliver subscriptions by email and/or Slack.
**Creating a subscription:**
1. Open the dashboard you want to subscribe to.
2. Click the settings menu (**...**) in the top right corner.
3. Select **Add/Manage Dashboard Subscription**.
4. Configure your delivery channel (email, Slack), recipients (emails or Slack channel), and schedule (for example, daily or weekly).
5. Click **Save**.
### Organize your dashboard
A well-organized dashboard conveys what information is most important and shows the relationship between items. You can move and resize dashboard widgets in place.
Each dashboard uses a grid layout. To move a widget, click and hold empty space on the widget and drag it to the target position. To resize a widget, click and hold the bottom right edge and drag to the target size.
### View and edit a chart in a dashboard
You can add any chart supported in Metrics Explorer to a dashboard. Dashboard charts are interactive, not static.
To explore a chart on the dashboard, click the **[ ]** icon. When expanded, you have access to the full Metrics Explorer and can modify the query, date range, and chart title. These changes are for exploration only and don't permanently alter the chart on the dashboard.
To save changes, configure the chart and click **Save** to update the existing chart, or **Save As** to create a new chart on the dashboard.
{% figure %}

{% /figure %}
## Tips
### Dashboard templates
Dashboard templates reduce time to insights. Statsig compiles industry-standard metrics so your data visualization focuses on critical success indicators. You can start with a template and add additional charts as needed. Statsig offers the following templates:
* Product Growth
* Use Statsig's built-in metrics like DAU, WAU, Stickiness, etc. to track long-term product health
* Feature Success
* Input a feature usage event, like form\_submit or purchase\_completed, to evaluate the success of your feature rollout with usage and retention metrics
* Optionally, you can add a feature adoption event or related feature flag to broaden the scope of your tracking
* B2B SaaS Topline Metrics
* Input a key feature event and user subscription events to monitor product usage, user retention and subscription conversion rates over time
* Web Analytics
* Available with Autocapture, track essential web metrics like page views, clicks, and sessions duration. Go to the [Autocapture guide](/guides/sidecar-experiments/measuring-experiments#using-autocapture) for more information.
* Create from Gate or Experiment
* Input your Gate or Experiment of interest to generate a focused dashboard with related monitoring metrics
* Create from Tag
* Utilize Statsig's project tagging to create and easily maintain a dashboard with the metrics, experiments, and feature flags you care about
### Finding dashboards
After you create a dashboard, the *Dashboards* tab offers several ways to find it:
1. In the *Dashboards* tab, click the search box and select **My dashboards** to find dashboards you created.
2. In the *Dashboards* tab, click the filter icon to scope to dashboards with specific tags or created by specific team members.
3. In the *Dashboards* tab, search for the dashboard name.
4. Anywhere in Statsig, open global search with **cmd+k** and type the dashboard name.
================================================================================
# Users Tab
URL: https://docs.statsig.com/product-analytics/users-tab
Updated: 2026-07-11
================================================================================
## How the Users Tab works
The Users section of Statsig gives you an event-by-event level understanding of how users (and other Unit IDs) use your product. This lets you diagnose issues and understand user behavior at a granular level using event logs, session streams, and session replays. It's also a central place to manage feature rollout and experiment overrides.
## Exploring the Users Tab
When you open the Users tab, enter a specific ID and select the matching Unit ID type. There are also many context-specific entry points to the Users Tab within Statsig.
A Users Tab query surfaces the following information:
* **Properties:** Additional context for this ID, including email, country, OS, and browser. Customers with user profiles can see all user profile properties for a given ID.
* **Log History & Event Details:** History of events and exposures for this ID, limited to 5000 rows. You can view these as an event log stream or a session stream.
* **Overrides:** The ability to manually override an ID into a given feature gate or experiment variant.
* **Session Replays:** A sample of session replays the user has triggered.
## Events
### Log Streams
Under the Events tab, you can view the events and exposures this user triggered in their event log stream. From here, you can diagnose which features this user was most interested in or which experiment group they were in.
{% figure %}
{% /figure %}
### Session Streams
Switch from **Log Stream** to **Session Stream** to get a session-centric view of all events the user triggered during each session. You can refine either view by changing the time window, hiding events, and filtering to show only events or experiment/gate exposures.
{% figure %}

{% /figure %}
## Overrides
Under the Overrides tab, you can override an ID into a given experiment variant or feature gate rollout. If there's already an active override for an ID on a particular entity, it appears in this section and you can modify it inline. Overrides set in the Users Tab sync to the "Overrides" section of the relevant entity, and you can edit or remove them from either interface.
## Session Replays
Under Session Replays, you can navigate to a sample of session replays the user has triggered for additional insight into their behavior. For more information, refer to [Session Replays](/session-replay/overview).
================================================================================
# Infra Analytics Overview
URL: https://docs.statsig.com/infra-analytics/overview
Updated: 2026-07-11
================================================================================
**[Infra Analytics](https://statsig.com/infra-analytics)** pulls in logs, metrics, and alerts so you get system-level observability in the same place you analyze product outcomes.
* Collect metrics and traces through OpenTelemetry (OTEL)
* Search and analyze logs to investigate issues
* Create alerts tied to service health
* Connect infrastructure signals to product analytics for a unified understanding of impact
## Key features
* **Logs Explorer**: Debug incidents with search, filters, and visualizations
* **Topline Alerts**: Catch regressions and anomalies with log/metric-based triggers
* **Metrics Explorer**: Inspect infrastructure metrics alongside product metrics
***
## Set up Infra Analytics
* [Set up OTEL ingestion](/infra-analytics/getting-started) to start sending logs and metrics
* Traces are in limited preview: contact Statsig in Slack to request access
================================================================================
# Getting Started with OTEL + Statsig
URL: https://docs.statsig.com/infra-analytics/getting-started
Updated: 2026-07-11
================================================================================
This guide helps you set up and send OpenTelemetry telemetry to Statsig so you can use Infra Analytics (Logs Explorer, Metrics Explorer, Alerts).
There are two common paths:
* Kubernetes/OpenTelemetry Collector: scrape logs and metrics from your cluster and export to Statsig. Go to [Open Telemetry Logs and Metrics](/server/concepts/open_telemetry) for a more complete guide.
* Applications: export traces, metrics, and logs to your OpenTelemetry Collector (or traces directly from TypeScript/Node). Go to the quick starts below.
{% callout type="info" %}
**Endpoint & Auth**
* Endpoint: `https://api.statsig.com/otlp`
* Auth header: `statsig-api-key: `
{% /callout %}
{% callout type="info" %}
Direct trace export to the Statsig OTLP endpoint is only available for TypeScript/Node. For all other languages, send traces to your OpenTelemetry Collector and forward from the Collector to Statsig over OTLP/HTTP.
{% /callout %}
{% callout type="info" %}
Need a deeper setup guide? Go to [Open Telemetry Logs and Metrics](/server/concepts/open_telemetry) for collector installation and configuration, and the [Traces Explorer quick start](/infra-analytics/send-traces) for language-specific trace examples.
{% /callout %}
***
## Application Telemetry quick starts
{% tabs %}
{% tab title="Node.js" %}
Install dependencies:
```bash
npm install --save \
@opentelemetry/sdk-node \
@opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-trace-otlp-http \
@opentelemetry/exporter-metrics-otlp-http \
@opentelemetry/api-logs \
@opentelemetry/sdk-logs \
@opentelemetry/exporter-logs-otlp-http \
@opentelemetry/resources \
@opentelemetry/semantic-conventions
```
Initialize OpenTelemetry (e.g., `instrumentation.js`):
```js
// instrumentation.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { resourceFromAttributes } = require('@opentelemetry/resources');
const { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } = require('@opentelemetry/semantic-conventions');
// import if you want to enable traces
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http');
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
// For troubleshooting, set the log level to DiagLogLevel.DEBUG
// const { diag, DiagConsoleLogger, DiagLogLevel } = require('@opentelemetry/api');
// diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);
const statsigKey = process.env.STATSIG_SERVER_SDK_SECRET;
const headers = { 'statsig-api-key': statsigKey ?? '' };
const sdk = new NodeSDK({
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: process.env.OTEL_SERVICE_NAME || 'statsig-node-service',
[ATTR_SERVICE_VERSION]: process.env.VERSION || '1',
env: process.env.NODE_ENV || 'development',
}),
traceExporter: new OTLPTraceExporter({
url: 'https://api.statsig.com/otlp/v1/traces',
// or
// url: /v1/traces
headers,
}),
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({
url: 'https://api.statsig.com/otlp/v1/metrics',
// or
// url: /v1/metrics
headers,
}),
exportIntervalMillis: 60000,
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
```
To set up application logs with OTel, use the pino or winston bridges. The example below uses [pino](https://getpino.io/#/) with
[pino auto instrumentation](https://www.npmjs.com/package/@opentelemetry/instrumentation-pino).
Install the pino instrumentation:
```bash
npm i pino @opentelemetry/instrumentation-pino
```
```js
// instrumentation.js (continued)
const { BatchLogRecordProcessor } = require('@opentelemetry/sdk-logs');
const statsigKey = process.env.STATSIG_SERVER_SDK_SECRET;
const headers = { 'statsig-api-key': statsigKey ?? '' };
const sdk = new NodeSDK({
// ... other config ...
logRecordProcessors: [
new BatchLogRecordProcessor(
new OTLPLogExporter({
url: 'https://api.statsig.com/otlp/v1/logs',
// or
// url: /v1/logs
headers
})
),
],
instrumentations: [getNodeAutoInstrumentations(), new PinoInstrumentation()],
});
// in your application code, e.g., app.js
const pino = require('pino');
const logger = pino();
logger.info('OTel logs initialized');
```
The Statsig SDK also supports forwarding logs to Log Explorer. Go to the alternative logging example below.
```js
// Requires: npm i @statsig/statsig-node-core
const { Statsig, StatsigUser } = require('@statsig/statsig-node-core');
const s = new Statsig(process.env.STATSIG_SERVER_SDK_SECRET);
await s.initialize();
const user = new StatsigUser({
userID: 'a-user',
custom: { service: process.env.OTEL_SERVICE_NAME || 'my-node-service' },
});
// levels: trace, debug, info, log, warn, error
s.forwardLogLineEvent(user, 'info', 'service started', { version: process.env.npm_package_version });
try {
// your app code
} catch (err) {
s.forwardLogLineEvent(user, 'error', 'unhandled error', {
message: String(err?.message || err),
stack: err?.stack,
});
}
```
Run your service:
Require or import `instrumentation.js` before any other application code to ensure instrumentation sets up correctly.
```bash
STATSIG_SERVER_SDK_SECRET=YOUR_SECRET \
OTEL_SERVICE_NAME=my-node-service \
node -r ./instrumentation.js app.js
```
Tip: you can configure exporters through environment variables instead of code:
* `OTEL_EXPORTER_OTLP_ENDPOINT=https://api.statsig.com/otlp`
* `OTEL_EXPORTER_OTLP_HEADERS=statsig-api-key=${STATSIG_SERVER_SDK_SECRET}`
* `OTEL_EXPORTER_OTLP_PROTOCOL=http/json`
{% /tab %}
{% tab title="Next.js" %}
Go to the official Next.js OpenTelemetry instructions for [pages router](https://nextjs.org/docs/pages/guides/open-telemetry) and for [app router](https://nextjs.org/docs/app/guides/open-telemetry).
Install dependencies:
```bash
npm install @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/semantic-conventions @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-http @opentelemetry/auto-instrumentations-node
```
Add `instrumentation.ts` at the app root (Next 13+):
```ts
// instrumentation.ts
import { NodeSDK } from '@opentelemetry/sdk-node';
import { resourceFromAttributes } from '@opentelemetry/resources';
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
// For troubleshooting, set the log level to DiagLogLevel.DEBUG
// const { diag, DiagConsoleLogger, DiagLogLevel } = require('@opentelemetry/api');
// diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);
export async function register() {
const headers = { 'statsig-api-key': process.env.STATSIG_SERVER_SDK_SECRET ?? '' };
const sdk = new NodeSDK({
resource: new Resource({
[ATTR_SERVICE_NAME]: process.env.OTEL_SERVICE_NAME || 'statsig-node-service',
[ATTR_SERVICE_VERSION]: process.env.VERSION || '1',
env: process.env.NODE_ENV || 'development',
}),
traceExporter: new OTLPTraceExporter({
url: 'https://api.statsig.com/otlp/v1/traces',
// or
// url: /v1/traces
headers,
}),
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({
url: 'https://api.statsig.com/otlp/v1/metrics',
// or
// url: /v1/metrics
headers,
}),
exportIntervalMillis: 60000,
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
}
```
To set up application logs with OTel, use the pino or winston bridges. The example below uses [Pino](https://getpino.io/#/) with
[Pino auto instrumentation](https://www.npmjs.com/package/@opentelemetry/instrumentation-pino).
Install the pino instrumentation:
```bash
npm i pino @opentelemetry/instrumentation-pino
```
```js
// instrumentation.ts (continued)
import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
const statsigKey = process.env.STATSIG_SERVER_SDK_SECRET;
const headers = { 'statsig-api-key': statsigKey ?? '' };
const sdk = new NodeSDK({
// ... other config ...
logRecordProcessors: [
new BatchLogRecordProcessor(
new OTLPLogExporter({
url: 'https://api.statsig.com/otlp/v1/logs',
// or
// url: /v1/logs
headers
})
),
],
instrumentations: [getNodeAutoInstrumentations(), new PinoInstrumentation()],
});
// in your application code, e.g., app.ts
import pino from 'pino';
const logger = pino();
logger.info('OTel logs initialized');
```
The Statsig SDK also supports forwarding logs to Log Explorer. Go to the alternative logging example below.
```js
// Requires: npm i @statsig/statsig-node-core
import { Statsig, StatsigUser } from '@statsig/statsig-node-core';
const s = new Statsig(process.env.STATSIG_SERVER_SDK_SECRET);
await s.initialize();
const user = new StatsigUser({
userID: 'a-user',
custom: { service: process.env.OTEL_SERVICE_NAME || 'my-node-service' },
});
// levels: trace, debug, info, log, warn, error
s.forwardLogLineEvent(user, 'info', 'service started', { version: process.env.npm_package_version });
try {
// your app code
} catch (err) {
s.forwardLogLineEvent(user, 'error', 'unhandled error', {
message: String(err?.message || err),
stack: err?.stack,
});
}
```
{% callout type="note" %}
In Next.js, mark '@statsig/statsig-node-core' as a server external package in `next.config.js` to avoid bundling.
{% /callout %}
### Using the Statsig + Vercel integration
If you deploy to Vercel, use the [Statsig + Vercel integration](/integrations/vercel/) to forward logs to Statsig automatically.
{% callout type="note" %}
* Keep `STATSIG_SERVER_SDK_SECRET` out of client bundles (do not use `NEXT_PUBLIC_`).
* Client/browser tracing requires separate web tracer setup; do not send secrets client-side. Consider routing through a Collector.
{% /callout %}
{% /tab %}
{% tab title="Other Languages/Frameworks" %}
Sending OTLP data directly to Statsig without a collector is only supported for Node.js applications. For other languages and frameworks, send OTLP data to a collector and configure the collector to forward data to Statsig.
Go to the [Collector quick starts](#collector-quick-starts) below for example configurations. For installation and configuration instructions for other languages and frameworks, go to the [OpenTelemetry Language APIs & SDKs documentation](https://opentelemetry.io/docs/languages/).
{% /tab %}
{% /tabs %}
***
## Collector quick starts
Running a Collector is optional but recommended for production workloads.
Use the OpenTelemetry Collector as a gateway to receive OTLP from your applications and forward to Statsig. This is useful for centralizing telemetry collection, adding advanced sampling methods like tail-based sampling, or scraping logs and metrics from hosts or Kubernetes.
{% tabs %}
{% tab title="Kubernetes (Helm)" %}
Create a minimal `values.yaml` for the OpenTelemetry Collector that forwards all signals (traces, metrics, logs) to Statsig:
```yaml title="values.yaml"
config:
receivers:
otlp:
protocols:
http:
grpc:
processors:
batch: {}
exporters:
otlphttp:
endpoint: https://api.statsig.com/otlp
encoding: json
headers:
statsig-api-key: ${env:STATSIG_SERVER_SDK_SECRET}
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp]
logs:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp]
```
Install the Collector with Helm:
```bash
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update
helm install otel-gateway open-telemetry/opentelemetry-collector \
-n otel --create-namespace \
-f values.yaml
```
Provide the Statsig key as an environment variable to the Collector pods (for example, through a Secret and envFrom). Your applications then send OTLP to the in-cluster Collector endpoint (for example `http://otel-gateway-collector.otel.svc.cluster.local:4318`).
For a production setup that also scrapes Kubernetes logs and metrics, go to the full guide: [Open Telemetry Logs and Metrics](/server/concepts/open_telemetry).
{% callout type="note" %}
**Version requirement**
The `encoding: json` option in the OTLP HTTP exporter requires Collector v0.95.0 or newer. If you pin the image through Helm values, set `image.tag: "0.95.0"` (or newer).
{% /callout %}
{% /tab %}
{% tab title="Docker (Compose)" %}
Use Docker Compose to run a Collector gateway that accepts OTLP and forwards to Statsig.
```yaml title="docker-compose.yaml"
services:
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
command: ["--config=/etc/otel-collector-config.yaml"]
environment:
- STATSIG_SERVER_SDK_SECRET=${STATSIG_SERVER_SDK_SECRET}
volumes:
- ./otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
```
Create the Collector config referenced above:
```yaml title="otel-collector-config.yaml"
receivers:
otlp:
protocols:
http:
grpc:
processors:
batch: {}
exporters:
otlphttp:
endpoint: https://api.statsig.com/otlp
encoding: json
headers:
statsig-api-key: ${env:STATSIG_SERVER_SDK_SECRET}
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp]
logs:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp]
```
Start the Collector:
```bash
export STATSIG_SERVER_SDK_SECRET=YOUR_SECRET
docker compose up -d
```
Point your applications at the Collector: `http://localhost:4318` (or `http://otel-collector:4318` from other compose services). The Collector forwards telemetry to Statsig using your key.
{% /tab %}
{% tab title="Other" %}
You can run the Collector in other environments (VMs, bare metal) using the config below. Go to the [Collector documentation](https://opentelemetry.io/docs/collector/installation/) for other installation and deployment methods.
```yaml title="otel-collector-config.yaml"
exporters:
otlphttp:
endpoint: https://api.statsig.com/otlp
encoding: json
headers:
statsig-api-key: ${env:STATSIG_SERVER_SDK_SECRET}
```
{% /tab %}
{% /tabs %}
***
## Common Collector configs (K8s & Docker)
The following examples show popular receivers and processors you can enable in your Collector and still export to Statsig through the same `otlphttp` exporter.
{% callout type="note" %}
These components live in the contrib distribution. Use an image that includes them:
* Docker: `otel/opentelemetry-collector-contrib` or newer
* Helm: set `image.repository: otel/opentelemetry-collector-contrib` (and a compatible `image.tag`)
{% /callout %}
Helm values (contrib image):
```yaml title="values.yaml"
image:
repository: otel/opentelemetry-collector-contrib
tag: "latest"
pullPolicy: IfNotPresent
```
### A. File logs (filelog receiver)
Reads and parses logs from files on disk. This is useful for hosts, containers, or Kubernetes nodes.
Minimal example:
```yaml
receivers:
filelog:
include: [ /var/log/myservice/*.json ]
start_at: beginning
operators:
- type: json_parser
timestamp:
parse_from: attributes.time
layout: '%Y-%m-%dT%H:%M:%S%z'
processors:
batch: {}
exporters:
otlphttp:
endpoint: https://api.statsig.com/otlp
encoding: json
headers:
statsig-api-key: ${env:STATSIG_SERVER_SDK_SECRET}
service:
pipelines:
logs:
receivers: [filelog]
processors: [batch]
exporters: [otlphttp]
```
Kubernetes tip: to tail container logs on nodes, mount host paths (for example, `/var/log/pods` and `/var/lib/docker/containers`) into the Collector DaemonSet and set `include` to those paths.
### B. EC2 resource detection (resourcedetection processor)
Automatically adds AWS EC2 metadata (cloud provider, region/zone, instance ID) to your telemetry.
```yaml
processors:
resourcedetection/ec2:
detectors: [env, ec2]
timeout: 2s
override: false
service:
pipelines:
traces:
receivers: [otlp]
processors: [resourcedetection/ec2, batch]
exporters: [otlphttp]
metrics:
receivers: [otlp]
processors: [resourcedetection/ec2, batch]
exporters: [otlphttp]
logs:
receivers: [otlp]
processors: [resourcedetection/ec2, batch]
exporters: [otlphttp]
```
The Collector must be able to reach the EC2 metadata service (IMDS). Ensure network access to `169.254.169.254` and IMDSv2 where required.
### C. Docker container metrics (docker\_stats receiver)
Emits container CPU, memory, network, and block I/O metrics by querying the Docker daemon.
```yaml
receivers:
docker_stats:
endpoint: unix:///var/run/docker.sock
collection_interval: 15s
processors:
batch: {}
exporters:
otlphttp:
endpoint: https://api.statsig.com/otlp
encoding: json
headers:
statsig-api-key: ${env:STATSIG_SERVER_SDK_SECRET}
service:
pipelines:
metrics:
receivers: [docker_stats]
processors: [batch]
exporters: [otlphttp]
```
Requirements:
* Linux only (not supported on macOS or Windows).
* Mount the Docker socket into the Collector container: `/var/run/docker.sock`.
***
## Related resources
* OpenTelemetry Collector: https://opentelemetry.io/docs/collector/
* Kubernetes Collector components: https://opentelemetry.io/docs/platforms/kubernetes/collector/components/
* Helm chart: https://github.com/open-telemetry/opentelemetry-helm-charts
* Collector configuration reference: https://opentelemetry.io/docs/collector/configuration
* OTLP protocol specification: https://opentelemetry.io/docs/specs/otlp/
* Filelog receiver (contrib): https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/filelogreceiver
* Resource detection processor (contrib): https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/resourcedetectionprocessor
* Docker stats receiver (contrib): https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/dockerstatsreceiver
* Collector contrib distribution: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib
================================================================================
# Traces Explorer Quick Start
URL: https://docs.statsig.com/infra-analytics/send-traces
Updated: 2026-07-11
================================================================================
{% callout type="info" %}
To set up Logs and Metrics as well, go to [Getting Started](/infra-analytics/getting-started) for more in-depth OpenTelemetry Collector setup instructions.
{% /callout %}
Use the OTLP/HTTP traces endpoint to forward spans into Traces Explorer. Authenticate using your Server Secret key in the header:
* Endpoint: `https://api.statsig.com/otlp/v1/traces`
* Header: `statsig-api-key: `
{% callout type="note" %}
Statsig currently supports direct to API for TypeScript/Node only.
For all other languages, send traces to your OpenTelemetry Collector and configure it to forward to Statsig over OTLP/HTTP.
{% /callout %}
For non-TypeScript apps, point your Collector (for example, `http://localhost:4318/v1/traces`) to Statsig by configuring the Collector to forward traffic to Statsig:
```yaml title="collector.yaml"
receivers:
otlp:
protocols:
http:
exporters:
otlphttp:
endpoint: https://api.statsig.com/otlp
encoding: json
headers:
statsig-api-key: ${env:STATSIG_SERVER_SDK_SECRET}
service:
pipelines:
traces:
receivers: [otlp]
exporters: [otlphttp]
```
{% tabs %}
{% tab title="TypeScript/Node" %}
```bash
npm install @opentelemetry/sdk-node @opentelemetry/sdk-trace-node @opentelemetry/sdk-trace-base @opentelemetry/exporter-trace-otlp-http @opentelemetry/resources @opentelemetry/semantic-conventions @opentelemetry/api
```
```js
// trace.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
const { BatchSpanProcessor } = require('@opentelemetry/sdk-trace-base');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
const { trace } = require('@opentelemetry/api');
const sdk = new NodeSDK({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'trace-sample-node',
}),
spanProcessor: new BatchSpanProcessor(
new OTLPTraceExporter({
url: 'https://api.statsig.com/otlp/v1/traces',
headers: { 'statsig-api-key': process.env.STATSIG_SERVER_SDK_SECRET || '' },
}),
),
});
sdk.start().then(() => {
const tracer = trace.getTracer('example');
const span = tracer.startSpan('do-work');
span.setAttribute('example', true);
span.end();
setTimeout(() => sdk.shutdown(), 1000);
});
```
{% /tab %}
{% tab title="Python" %}
```bash
pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
```
```python
# trace.py
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
trace.set_tracer_provider(
TracerProvider(resource=Resource.create({"service.name": "trace-sample-python"}))
)
exporter = OTLPSpanExporter(
endpoint="http://localhost:4318/v1/traces", # your Collector
)
trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(exporter))
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span("do-work") as span:
span.set_attribute("example", True)
trace.get_tracer_provider().shutdown()
```
{% /tab %}
{% tab title="Go" %}
```bash
go get go.opentelemetry.io/otel/sdk go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp go.opentelemetry.io/otel/semconv/v1.26.0
```
```go
// main.go
package main
import (
"context"
"log"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
func main() {
ctx := context.Background()
exporter, err := otlptracehttp.New(ctx,
otlptracehttp.WithEndpointURL("http://localhost:4318/v1/traces"), // your Collector
)
if err != nil {
log.Fatal(err)
}
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter),
sdktrace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("trace-sample-go"),
)),
)
otel.SetTracerProvider(tp)
tracer := otel.Tracer("example")
ctx, span := tracer.Start(ctx, "do-work")
span.SetAttributes(attribute.Bool("example", true))
span.End()
_ = tp.Shutdown(ctx)
}
```
{% /tab %}
{% /tabs %}
{% callout type="info" %}
For collector installation and configuration, go to [Open Telemetry Logs and Metrics](/server/concepts/open_telemetry). For full Logs and Metrics setup, go to [Getting Started](/infra-analytics/getting-started).
{% /callout %}
================================================================================
# Logs Explorer Overview
URL: https://docs.statsig.com/infra-analytics/logs-explorer
Updated: 2026-07-11
================================================================================
Logs Explorer lets you query logs, traces, and ingested events from a single interface. Use it the same way whether you’re debugging infrastructure issues or investigating product event streams.
* **Search**: Slice logs down to only what's relevant (by service, host, status code, etc.)
* **Group**: Aggregate logs by dimensions like region, status, or browser.
* **Visualize**: Plot log groupings over time to spot spikes, regressions, or anomalies instantly.
***
### Getting started with Log Explorer
To get started with Log Explorer, follow the [OTEL onboarding guide](/infra-analytics/getting-started) to set up log ingestion. After you configure log ingestion, navigate to *Infra Analytics > Log Explorer* from the Statsig left menu.
You can also use Logs Explorer in [Events Mode](/infra-analytics/events-mode-logs-explorer) to search and analyze your existing Statsig Events, with no additional instrumentation needed. You can switch between Logs and Events mode using the dropdown left of the search bar.
{% figure %}

{% /figure %}
***
### Searching in Logs Explorer
* **Write custom queries**: Refer to the [syntax guide](/infra-analytics/logs-explorer-queries) to craft your search.
* **Using the query builder**: Point-and-click to construct filters without syntax overhead.
{% figure %}

{% /figure %}
***
### Download log results
Export your query results to a file when you need to share findings or run them through a script. The **Download** button lives in the Logs Explorer toolbar, in the top-right corner next to the settings gear. Downloads work in both Logs and Events mode.
To download results:
1. Run your query.
2. Open the **Download** menu from the toolbar.
3. Choose a format and row cap, then start the download.
A progress indicator shows the export as it runs, and you can cancel mid-download.
**Formats**
* **CSV**: Columns match the visible table. The download excludes hidden columns, so the file reflects what you see on screen.
* **JSONL**: Emits one full log object per line, which works well with tools like `grep` and `jq`.
**Row caps**
Choose a cap of 100, 1,000, or 10,000 rows. The default is 1,000; select 10,000 for heavier pulls.
================================================================================
# Query Syntax for Logs Explorer
URL: https://docs.statsig.com/infra-analytics/logs-explorer-queries
Updated: 2026-07-11
================================================================================
This page covers common syntax you can use in day-to-day investigations, plus a few ready-to-edit examples.
{% callout type="info" %}
Plain text searches will only match against the **log message field**, not the entire log body.
{% /callout %}
## Basics
### Property prefixes
| Prefix | Description | Example |
|--------|-------------|---------|
| `@property` | Event or log properties | `@traceId:"1a3cg5"` |
| `#property` | Reserved properties | `#custom_event:"shopping_cart_opened"` |
| `$user` | User identifiers | `$stableID:"abcdef"` |
| *none* | User properties | `tier:prod` |
### Logical operators
These are query-level connectors. They combine or negate multiple conditions.
| Operator | Description | Example |
|----------|-------------|---------|
| `AND` (case sensitive) | Match all conditions | `level:error AND service:api-gateway` |
| `OR` (case sensitive) | Match any condition | `level:error OR level:warn` |
| `!=` or `!:` | Exclude a condition | `status!:error OR service!:api-gateway` |
### Other operators
These are field-level conditions.
| Operator | Description | Example |
|----------|-------------|---------|
| `:` or `=` | equals | `status_code:200` or `status_code=200` |
| `!=` or `!:` | not equals | `level!:debug` or `level!=debug` |
| `>` `<` | greater/less than | `latency_ms>500` or `latency_ms<1000` |
| `>=` `<=` | greater/less or equal | `latency_ms>=500` or `latency_ms<=2000` |
### Wildcard search
You can use the `*` character as a wildcard in queries. A wildcard matches zero or more characters inside a field value.
{% callout type="note" %}
Wildcards can impact query performance. Use them sparingly.
{% /callout %}
### Additional examples
| Query | Description | Matches |
|-------|-------------|---------|
| `@"log.file.path":"/logs/pods/*"` | Path begins with `/logs/pods/` | `/logs/pods/1234/stdout.log` |
| `@"log.file.path":"*/logs/pods/"` | Path ends with `/logs/pods/` | `/mnt/storage/logs/pods/` |
| `service:"api-*"` | Services with names starting with `api-` | `api-gateway`, `api-auth` |
| `@route:"*products/*"` | Page routes containing the string `products/` | `shopify.com/products/tshirts`, `shopify.com/cart/products/view` |
| `@message!:""` | Return logs where the message field is **not null** | any log that has a `message` field |
================================================================================
# Events Mode on Logs Explorer
URL: https://docs.statsig.com/infra-analytics/events-mode-logs-explorer
Updated: 2026-07-11
================================================================================
Events Mode brings the searching and filtering abilities from [Log Explorer](/infra-analytics/logs-explorer) to your *existing* Statsig events data. No additional instrumentation required!
* Debug user activity with more control
* Trace the sequence of actions a user performed
* Narrow in on specific events from metadata properties
{% callout type="info" %}
Events Mode is available to all **Statsig Cloud customers**.
{% /callout %}
***
### Get started with Events Mode
1. Open Logs Explorer from the left navigation.
2. Confirm you've switched from **Logs** mode to **Events** mode (the area next to the search bar should say **Events**).
3. Start searching across event names, properties, and time ranges.
***
### Construct a search
You can search through events in two ways:
1. **Writing queries:** Use [syntax-based search](/infra-analytics/logs-explorer-queries) to target specific events and properties
2. **Using the query builder:** Point-and-click to construct filters without syntax overhead.

***
### Example workflows
| Description | Query |
|-------------------------------------------------------|----------------------------------------------------------------------|
| Find all enterprise account signups from web browser devices | `event_name:signup_completed AND #plan:enterprise AND #platform:web` |
| Find all checkout events where the item name contained "black" | `@#custom_event:checkout_event,add_to_cart AND @product:"*black*"` |
| Find all events from users with a gmail account | `#user_object.email:"*gmail.com"` |
================================================================================
# Topline Alerts with Logs
URL: https://docs.statsig.com/infra-analytics/topline-alerts-logs
Updated: 2026-07-11
================================================================================
By combining log filters with [Topline Alerts](/product-analytics/topline-alerts), you can detect failures immediately, without writing custom scripts or dashboards.
### When to use log-based alerts
- **Monitoring success rates** - Catch regressions before they impact SLOs.
- **Detecting error spikes** - Trigger when 5xx or other errors rise above baseline.
- **Isolate by segment** - Identify failures concentrated in a region, client, or device type
## Create a log-based Topline Alert (Statsig Cloud)
This example creates a monitor for the success rate of a GET request.
{% steps %}
{% step title="Navigate to Topline Alerts" %}
- Go to **Analytics → Topline Alerts** in the product menu.
- Click **+Create**.
- Enter a name for your alert.
{% figure %}

{% /figure %}
{% /step %}
{% step title="Define Alert Criteria" %}
- Select `statsig::log_line` as your event.
- Apply filters to define what constitutes success and failure.
If you're unsure which fields to filter on, open the [Logs Explorer](/infra-analytics/logs-explorer) and inspect the log body.
{% figure %}

{% /figure %}
{% /step %}
{% step title="Set Up Evaluation Formula" %}
- Define the formula for calculating your success rate.
- (Optional) Add a group-by dimension.
{% figure %}

{% /figure %}
{% /step %}
{% step title="Configure Alert Thresholds" %}
- Set thresholds and the evaluation window.
In this example, Statsig triggers a warning when success rate drops below 99.5% and an alert when success rate drops below 99.0%.
{% figure %}

{% /figure %}
{% /step %}
{% step title="Configure Notifications" %}
- Add the alert title and description for context.
- Include diagnostic hints (e.g. "Check version X" or "Android requests timing out").
- Add subscribers.
- Set priority.
{% figure %}

{% /figure %}
{% /step %}
{% step title="Save the Alert" %}
- Click **Save**.
- After you save your alert, visit the **Diagnostics** tab to view a history of alert triggers.
{% figure %}

{% /figure %}
{% /step %}
{% /steps %}
---
## Best practices
- Set up [Slack notifications](/integrations/slack) for team visibility
- Keep formulas simple (ratios & percentages are easiest to scan)
- Add group-by dimensions (like country or app version) to pinpoint where issues occur
- Write clear notification text that explains what the alert means
================================================================================
# Alerts Overview
URL: https://docs.statsig.com/product-analytics/alerts-overview
Updated: 2026-07-11
================================================================================
## Alerts
Statsig offers two types of alerts on the platform:
1. **[Topline Metric Alerts](/product-analytics/topline-alerts)** - Monitor a metric’s overall performance, independent of experiments or gates. These alerts keep you informed about topline trends and highlight when key product or business metrics move in a concerning direction.
2. **[Rollout Alerts](/product-analytics/alerts-overview)** - Monitor how a metric behaves in the context of a feature gate or experiment rollout. These alerts help you confirm that no critical metrics regress when introducing a new change.
================================================================================
# Rollout Alerts
URL: https://docs.statsig.com/metrics/rollout-alerts
Updated: 2026-07-11
================================================================================
## How rollout alerts work
On Statsig Cloud, Rollout Alerts are evaluated every 24 hours. Alerts only trigger if the cumulative metric delta is statistically significantly lower or higher than your threshold, which reduces alert noise.
On Statsig Warehouse Native, Rollout Alerts are evaluated every time Metric Results are loaded in a feature gate or experiment. Loading Results on the first day provides more real-time visibility during this window. Alerts only trigger if the metric delta is statistically significantly lower or higher than your threshold, which reduces alert noise.
Statsig applies all stats methodologies you have enabled for your experiment or gate rollout (CUPED, Sequential Testing, and so on) to alert calculations.
{% callout type="note" %}
Rollout Alerts do not alert at the
**topline metric value**
level, but rather at
**the experiment/feature gate level**
. This means that even if you have an experiment allocated to 10% of your users, but the metric change within that 10% allocation breaches the set threshold, Statsig alerts you. All alerts you receive are in the context of a specific experiment or feature gate, and to debug/resolve the alert you are directed to the offending experiment or gate in question.
{% /callout %}
## Setting up a rollout alert
To set up a metric alert, go to the **Metrics** tab, then **Metrics Catalog**, and search for the metric you want.
{% figure %}
{% /figure %}
After you open the Metric Detail View, go to the **Alerts** tab and tap **+ Create Alert**. Configure the following inputs:
- **% Change:** The metric delta threshold that triggers an automated alert. This delta is measured in the context of the feature gate rollout or experiment, not as a top-line metric change across your entire user base.
- **Minimum Participating Units:** The minimum number of unique units emitting the metric in each test group before triggering an alert. Statsig displays the 25th, 50th, and 75th percentiles of metric-emitting unit counts per group across your gates and experiments to help you choose a reasonable threshold.
- **Direction:** Positive or negative, depending on whether you want to be alerted when the metric exceeds or drops below a target threshold.
- **Subscribers:** By default, all creators of a feature gate or experiment with an alert configured are notified when the alert fires. You can also add global subscribers, who are notified when any feature gate or experiment regresses the metric beyond the threshold. In a large project, adding yourself as a global subscriber of a metric can generate high alert volume.
{% figure %}
{% /figure %}
After you configure a Rollout Alert for a metric, an alarm bell icon appears next to the metric in the Metrics Catalog. You can also filter for metrics with alerts using the standard metrics filter next to the search bar.
## Determining the right threshold
To help you configure the right threshold, the Scorecard shows a preview of how much the metric has moved in the context of any feature gates or experiments containing that metric.
{% figure %}

{% /figure %}
To see how a metric has trended over a longer period, hover over the metric delta for a feature gate or experiment and tap a data point to view more details. This opens the time-series for the metric, with a configurable date range picker.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
## Alert notifications and resolution
When a Rollout Alert triggers, all subscribers and the relevant gate or experiment creator(s) receive a notification by email, in the Statsig Console, and through Slack for users who have configured Slack notifications for their Statsig accounts.
{% figure %}
{% /figure %}
{% figure %}
{% /figure %}
Tapping **View Alert** takes you to the Diagnostics page of the offending feature gate or experiment that triggered the rollout alert.
{% figure %}
{% /figure %}
In the Diagnostics section, scroll to the **Rollout Alerts** section and select the active alert. The metric delta trend appears with the threshold overlaid. To resolve the alert, tap **Resolve** inline. You can either resolve the alert and provide an explanation, or snooze the alert for a specified period. The alert status updates both in the **Rollout Alerts** section and at the top of the Pulse page for the gate or experiment.
{% figure %}
{% /figure %}
## Viewing alert history
To view alert history, go to the **Metrics** tab, then **Metrics Catalog**, and select the metric. Then go to **Alerts**, open the **…** menu in the **Experiment and Gate Alerts** section, and select **View Alert History**. The history shows all instances of the alert firing, being resolved, or being snoozed, along with the resolution reason, who resolved the alert, and when.
{% figure %}
{% /figure %}
{% figure %}
{% /figure %}
================================================================================
# Topline Alerts
URL: https://docs.statsig.com/product-analytics/topline-alerts
Updated: 2026-07-11
================================================================================
{% callout type="info" %}
Reach out if you would like Topline Alerts enabled for your project.
{% /callout %}
Topline Alerts are available in Statsig Cloud and Statsig Warehouse Native with support available for three types of Topline Alerts:
| Alert Condition Type | When to Use | Example |
| -------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------- |
| **Threshold** | Use when you want to stay above or below a fixed number. | “Alert me when P90 latency spikes above 15 seconds.” |
| **Change** | Use when the absolute size of the change matters. | “Alert me when hourly P90 latency increases by 10 seconds.” |
| **Change (%)** | Use when the relative size of the change matters more than raw numbers. | “Alert me when hourly P90 latency increases by 50%.” |
## Creating a topline alert
{% steps %}
{% step title="Navigate to Topline Alerts" %}
Go to **Analytics → Topline Alerts** in the product menu.
{% /step %}
{% step title="Create a New Alert" %}
Click **+Create** and give the new alert a name.
{% /step %}
{% step title="Select the Event or Metric Source" %}
Pick the data you want to monitor.
- **On Statsig Warehouse Native:** Select a Metric Source, filter, and group by the dimensions you want.
- **On Statsig Cloud:** Select the event, aggregation, filters, and group-by conditions for this alert.
{% callout type="info" %}
This example sets up an alert to monitor P90 latency for `mex_query` events, filtering out internal employee queries and grouping by the `hadGroupBy` dimension.
{% /callout %}
{% figure %}

{% /figure %}
{% /step %}
{% step title="Review the Alert Preview" %}
The preview shows how your metric is trending with the current setup. Confirm values look correct, or open the metric in [Metrics Explorer](/product-analytics/drilldown) for deeper analysis.
{% figure %}

{% /figure %}
{% /step %}
{% step title="Set Alert Conditions" %}
The preview updates along with each change. Define the:
- Condition type (threshold, change, change %)
- Directionality
- _Alert_ and _Warn_ values
- Evaluation window
{% callout type="warning" %}
On Warehouse Native: Define the evaluation frequency, lookback, and max delay. These values directly influence warehouse compute costs.
{% /callout %}
{% figure %}

{% /figure %}
{% /step %}
{% step title="Add Notifications" %}
Notifications go to email, the Statsig Console, and Slack (if connected). Project-wide defaults live in Settings.
- Draft a clear, actionable message subscribers receive when the alert fires.
- Add subscribers.
- Set alert priority.
- Configure re-notification rules if alerts should resend while conditions hold.
{% /step %}
{% step title="Save and Monitor" %}
After saving, triggered alerts appear at the top of the page. From here you can:
- View samples of the event.
- Open the trend in [Metrics Explorer](/product-analytics/drilldown).
- Mute the alert temporarily if it is noisy or already under investigation.
{% figure %}

{% /figure %}
{% /step %}
{% /steps %}
## Diagnostics
Navigate to the **Diagnostics** tab to review alert history, inspect samples, open Metrics Explorer, or mute noisy alerts.
{% figure %}

{% /figure %}
---
## Related resources
- [Create a Topline Alert on log lines](/infra-analytics/topline-alerts-logs)
- [Set up team Slack notifications](/integrations/slack/#team-notifications)
================================================================================
# Statsig Web Analytics Overview
URL: https://docs.statsig.com/webanalytics/overview
Updated: 2026-07-11
================================================================================
## Web analytics overview
[Web analytics](https://www.statsig.com/web-analytics) lets you track and watch key measures for your website easily. It is different from product analytics because it's simpler and more direct, making it great for marketers, web site maintainers, or anyone familiar with tools like Google Analytics.
With Web Analytics and Statsig Dashboards you can easily gather insights such as number of visitors, views, sessions, how long sessions last, error rates, usage journey, and more.
After you add web analytics to your application, events appear in your metrics page: https://console.statsig.com/metrics/events
{% figure %}

{% /figure %}
## Getting started
### Option 1: HTML script installation
Add a single script tag to your website:
```html
```
Get YOUR\_CLIENT\_KEY from *Project Settings > Keys & Environments*. Reveal the Client API Key, copy, and paste it over the \[YOUR\_CLIENT\_KEY] in the snippet above.

### Option 2: HTML script with advanced control
For more control over initialization and configuration, you can manually initialize the Statsig client:
```html
```
### Option 3: JavaScript client SDK (React bindings)
If using the JavaScript client SDK or another framework, use the corresponding SDK with the autocapture plugin (JS, React, Angular, etc.):
```javascript
import * as React from 'react';
import { StatsigProvider } from '@statsig/react-bindings';
import { StatsigAutoCapturePlugin } from '@statsig/web-analytics';
return (
Loading...
}
options={{ plugins: [ new StatsigAutoCapturePlugin() ] }}>
Hello
);
```
## Autocaptured events
Statsig Web Analytics automatically captures the following events without any additional code:
### Page navigation events
* **`auto_capture::page_view`** - Triggered when a user navigates to a new page. Includes page URL, query parameters, referrer information, and metadata.
* **`auto_capture::page_view_end`** - Triggered when a user leaves a page. Includes engagement metrics like scroll depth, time on page, and whether the exit was due to inactivity.
### User interaction events
* **`auto_capture::click`** - Captures all click events with target element information, coordinates, and page context.
* **`auto_capture::rage_click`** - Detects rapid repeated clicks in the same area, often indicating user frustration.
* **`auto_capture::dead_click`** - Identifies clicks that don't result in any page changes or navigation, potentially indicating broken functionality.
* **`auto_capture::form_submit`** - Captures form submission events with form metadata.
* **`auto_capture::copy`** - Tracks when users copy text from your page, including the selected text content.
### Technical performance events
* **`auto_capture::error`** - Automatically captures JavaScript errors with stack traces, error messages, and context.
* **`auto_capture::web_vitals`** - Captures Core Web Vitals metrics (refer to the [Web vitals events](#web-vitals-events) section below).
* **`auto_capture::performance`** - Collects page load performance metrics including load times, DOM interactive time, and network transfer data. (deprecated, use `auto_capture::web_vitals` instead)
### Session events
* **`auto_capture::session_start`** - Marks the beginning of a new user session.
### Console logging (disabled by default)
* **`statsig::log_line`** - Captures console log output when explicitly enabled. This event is disabled by default and must be configured to activate.
For details on how to enable console log autocapture, refer to [Console Log Capture Configuration](#console-log-capture-configuration).
## Event filtering and console configuration
### Disabling events through the console
You can disable specific autocapture events from the Statsig console under *Project Settings > Analytics & Session Replay*:

### Programmatic event filtering
For more granular control, you can filter events programmatically using a custom filter function:
```javascript
const client = new StatsigClient(
'YOUR_CLIENT_KEY',
{ userID: 'optional' },
{ plugins: [
new StatsigAutoCapturePlugin({
eventFilterFunc: (event) => {
// Filter out events from admin pages
if (event.metadata.pageUrl &&
event.metadata.pageUrl.includes('/admin/')) {
return false;
}
return true;
}
})
]}
);
```
### Console log capture configuration
Console log capture is disabled by default but you can enable it with the following configuration:
```javascript
const client = new StatsigClient(
'YOUR_CLIENT_KEY',
{ userID: 'optional' },
{ plugins: [
new StatsigAutoCapturePlugin({
consoleLogAutoCaptureSettings: {
enabled: true,
}
})
]}
);
```
There are a few additional optional configuration settings available when capturing console logs:
* **`logLevel`**: minimum level to capture (`debug` | `info` | `log` | `warn` | `error`)
* **`sampleRate`**: fraction between 0 and 1 to sample captured logs
* **`maxKeys`**: maximum number of keys to serialize from logged objects
* **`maxDepth`**: maximum nesting depth when serializing objects
* **`maxStringLength`**: maximum number of characters for stringified values
```javascript
const client = new StatsigClient(
'YOUR_CLIENT_KEY',
{ userID: 'optional' },
{ plugins: [
new StatsigAutoCapturePlugin({
consoleLogAutoCaptureSettings: {
enabled: true,
logLevel: 'warn', // capture warn and error
sampleRate: 0.5, // capture 50% of matched logs
maxKeys: 50, // limit number of keys per object
maxDepth: 3, // limit nested object depth
maxStringLength: 2000, // truncate long strings
}
})
]}
);
```
## Web vitals events
Statsig automatically captures Core Web Vitals metrics that are essential for measuring user experience and SEO performance:
### Core Web Vitals metrics
* **CLS (Cumulative Layout Shift)** - Measures visual stability by tracking unexpected layout shifts during page load. Lower scores indicate better user experience.
* **FCP (First Contentful Paint)** - Measures loading performance by tracking when the first text or image is painted on the screen. Faster FCP times indicate better perceived performance.
* **LCP (Largest Contentful Paint)** - Measures loading performance by tracking when the largest content element becomes visible. This metric correlates strongly with user-perceived load times.
* **TTFB (Time to First Byte)** - Measures server response time by tracking how long it takes to receive the first byte of response from the server. This indicates server and network performance.
* **INP (Interaction to Next Paint)** - Measures responsiveness by tracking the longest latency across user interactions. Lower values mean more consistent, responsive experiences.
Statsig automatically collects and sends these metrics as `auto_capture::web_vitals` events, providing insights into your website's performance and user experience quality.
## Single page application (SPA) support
Statsig Web Analytics automatically supports single page applications without any additional configuration. Statsig intelligently detects route changes through:
### Automatic route change detection
* **Browser Navigation**: Listens for `popstate` events to detect when users use browser back/forward buttons
* **Programmatic Navigation**: Automatically proxies `history.pushState` calls to detect when your application programmatically navigates to new routes
* **Automatic Page Views**: Triggers new `auto_capture::page_view` events whenever route changes are detected
Modern SPA frameworks like React Router, Vue Router, and Angular Router work with Statsig Web Analytics without requiring manual page view tracking.
## Exploring events in Metrics explorer
In [Metrics Explorer](https://console.statsig.com/metrics/explore), you can filter and group your events using complex filters and dimensions, then add the analysis to your dashboard.
For more on Metrics Explorer features like Funnels and Retention, refer to the [Product Analytics overview](/product-analytics/overview).

For example autocapture event details, refer to [Autocapture Metrics](/webanalytics/autocapture).
## Autocapture settings
The autocapture plugin or manual setup can take an optional `options` parameter to customize the autocapture settings.
* **`eventFilterFunc`**: `Optional[Callable[[AutoCaptureEvent], bool]]` - A function to filter events based on their metadata.
* **`consoleLogAutoCaptureSettings`**: `Optional[ConsoleLogAutoCaptureSettings]` - An object to configure console log capture.
**consoleLogAutoCaptureSettings**:
* **`enabled`**: `Optional[bool]` - A boolean to enable or disable console log capture.
* **`logLevel`**: `Optional[str]` - A string to set the minimum log level for console log capture.
* **`sampleRate`**: `Optional[float]` - A number to set the sample rate for console log capture. (0-1)
* **`maxKeys`**: `Optional[number]` - Maximum number of keys to serialize from logged objects.
* **`maxDepth`**: `Optional[number]` - Maximum nesting depth when serializing objects.
* **`maxStringLength`**: `Optional[number]` - Maximum number of characters for stringified values.
**eventFilterFunc**:
* **`event`**: `AutoCaptureEvent` - The event object Statsig is capturing.
* **`return`**: `bool` - A boolean to return true to capture the event or false to ignore it.
================================================================================
# Autocapture Metrics
URL: https://docs.statsig.com/webanalytics/autocapture
Updated: 2026-07-11
================================================================================
The JavaScript, React, and Angular SDKs support autocapture, which automatically captures events and their attributes without requiring you to manually instrument your code.
## Attributes captured
### Common metadata (on all events)
Statsig automatically captures comprehensive metadata with each autocapture event to provide rich context for analysis:
* To filter events, you can use the `eventFilterFunc` option when initializing the Statsig client.
* To sanitize events and their metadata, listen for the `pre_logs_flushed` client event and modify the event object before Statsig sends it. (Refer to [Client Event Emitter](/client/javascript-sdk#client-event-emitter) for more details.)
#### Page and environment metadata
| Property | Description |
| --- | --- |
| `metadata.title` | Page title from `` tag |
| `metadata.current_url` | Full current URL |
| `metadata.hostname` | Domain name of the current page |
| `metadata.pathname` | Path portion of the URL |
| `metadata.user_agent` | Browser user agent string (truncated to 200 chars) |
| `metadata.locale` | Browser language setting |
| `metadata.timezone` | User's timezone |
| `metadata.timezone_offset` | Timezone offset in minutes |
| `metadata.timestamp` | Event timestamp in milliseconds |
#### Screen and viewport data
| Property | Description |
| --- | --- |
| `metadata.screen_width` | Screen width in pixels |
| `metadata.screen_height` | Screen height in pixels |
| `metadata.viewport_width` | Browser viewport width in pixels |
| `metadata.viewport_height` | Browser viewport height in pixels |
#### Network information
| Property | Description |
| --- | --- |
| `metadata.effective_connection_type` | Connection speed estimate |
| `metadata.rtt_ms` | Round-trip time in milliseconds |
| `metadata.downlink_mbps` | Download speed in Mbps |
| `metadata.save_data` | Whether user has data saver enabled |
#### Referrer and traffic source data
| Property | Description |
| --- | --- |
| `metadata.referrer` | Full referrer URL |
| `metadata.referrer_domain` | Domain of the referrer |
| `metadata.referrer_path` | Path of the referrer URL |
| `metadata.searchEngine` | Detected search engine |
| `metadata.searchQuery` | Search query from referrer |
#### Campaign and marketing attribution
Statsig automatically captures 25+ marketing attribution parameters including:
| Property | Description |
| --- | --- |
| `metadata.utm_source` | UTM source parameter |
| `metadata.utm_medium` | UTM medium parameter |
| `metadata.utm_campaign` | UTM campaign parameter |
| `metadata.utm_term` | UTM term parameter |
| `metadata.utm_content` | UTM content parameter |
| `metadata.utm_id` | UTM ID parameter |
| `metadata.gclid` | Google Click ID |
| `metadata.fbclid` | Facebook Click ID |
| `metadata.msclkid` | Microsoft Bing Click ID |
| `metadata.ttclid` | TikTok Click ID |
| `metadata.li_fat_id` | LinkedIn Click ID |
| `metadata.epik` | Pinterest Click ID |
Additional platform IDs captured:
* `gclsrc`, `wbraid`, `gad_source` (Google)
* `dclid` (DoubleClick)
* `irclid` (Impact)
* `igshid` (Instagram)
* `_kx` (Klaviyo)
* `mc_cid`, `mc_eid` (Mailchimp)
* `qclid` (Quora)
* `rdt_cid` (Reddit)
* `sccid` (Snapchat)
* `ttc`, `ttc_id` (TikTok)
* `twclid` (Twitter)
### Action events (click, form submit, etc.)
| Property | Description |
| --- | --- |
| `tagName` | The HTML tag of the clicked element |
| `classList` | The list of CSS classes on the clicked element |
| `class` | The class attribute of the clicked element |
| `id` | The ID attribute of the clicked element |
| `ariaLabel` | The aria-label attribute of the clicked element |
| `selector` | A CSS selector that uniquely identifies the clicked element |
Depending on the event element, the following properties may be captured:
* `action`
* `method`
* `formName`
* `formId`
* `href`
* `content`
* `inputName`
* `textContent`
* `selectedText`
...and more
### Page view events
PageViewStart:
| Property | Description |
| --- | --- |
| `last_page_view_url` | The URL of the last page view |
PageViewEnd:
| Property | Description |
| --- | --- |
| `page_view_length` | The length of the page view in milliseconds |
| `lastScrollY` | The last scroll position in pixels |
| `maxScrollY` | The maximum scroll position reached during the page view in pixels |
| `lastScrollPercentage` | The last scroll percentage (0-100) of the page |
| `maxScrollPercentage` | The maximum scroll percentage reached during the page view (0-100) |
| `scrollDepth` | The scroll depth percentage (0-100) - deprecated, use maxScrollPercentage instead |
| `dueToInactivity` | Whether the page view ended due to inactivity |
### Dead click events
| Property | Description |
| --- | --- |
| `scrollTimeout` | Boolean indicating if a scroll timeout occurred |
| `selectionChangeTimeout` | Boolean indicating if a selection change timeout occurred |
| `mutationTimeout` | Boolean indicating if a mutation timeout occurred |
| `absoluteTimeout` | Boolean indicating if an absolute timeout occurred |
| `scrollDelayMs` | The number of milliseconds to wait before considering a scroll event as a dead click |
| `selectionChangeDelayMs` | The number of milliseconds to wait before considering a selection change event as a dead click |
| `mutationDelayMs` | The number of milliseconds to wait before considering a mutation event as a dead click |
| `absoluteDelayMs` | The number of milliseconds to wait before considering an absolute timeout event as a dead click |
### Rage click events
| Property | Description |
| --- | --- |
| `x` | The x-coordinate of the rage click |
| `y` | The y-coordinate of the rage click |
| `timestamp` | The timestamp of the rage click in milliseconds |
### Web vitals events
| Property | Description |
| --- | --- |
| `name` | The name of the web vital ('CLS', 'FCP', 'LCP', 'TTFB') |
| `value` | The current measurement value (e.g., milliseconds for LCP, FCP, etc.; a numeric score for CLS) |
| `delta` | The change from the previously reported value, used to calculate cumulative values over time |
| `id` | The ID of the web vital event|
### Console log events
| Property | Description |
| --- | --- |
| `status` | The status of the console log ('debug', 'info', 'warn', 'error') |
| `log_level` | The level of the console log ('debug', 'info', 'log', 'warn', 'error') |
| `payload` | The payload of the console log (array of strings) |
| `timestamp` | The timestamp of the console log in milliseconds (epoch time) |
| `trace` | A stack trace (if available) showing where in the code the console log was triggered (array of strings) |
| `source` | The source of the console log ('js-auto-capture') |
#### Query parameters
All URL query parameters are automatically captured as individual metadata fields with their parameter names as keys.
================================================================================
# AI Evals Overview
URL: https://docs.statsig.com/ai-evals/overview
Updated: 2026-07-11
================================================================================
{% callout type="info" %}
Statsig isn't accepting new customers for AI Evals at this time.
{% /callout %}
## What are AI evals?
Statsig AI Evals has three core components for iterating on and serving LLM apps in production.
1. **[Prompts](/ai-evals/prompts)**: Prompts represent an LLM prompt and its associated config (model provider, model, temperature, and similar settings). A prompt typically represents a task for the LLM (for example, "Classify this ticket to a triage queue" or "Summarize this text"). You can version prompts, choose which version is live in Statsig, and retrieve prompts in production using the Statsig server SDKs. Prompts can be used as the control plane for your LLM apps without using the rest of the Evals product suite.
2. **[Offline Evals](/ai-evals/offline-evals)**: Offline evals provide quick, automated grading of model outputs on a fixed test set. They catch wins and regressions before any real users are exposed. For example, compare a new support bot's replies to human-curated answers to decide if the bot is ready to ship. You can grade output even without a reference dataset (for example, when using an LLM to validate English-to-French translation).
3. **[Online Evals](/ai-evals/online-evals)**: Online evals let you grade model output in production on real-world use cases. You can run the "live" version of a prompt and also shadow-run "candidate" versions without exposing users to them. Grading works directly on the model output and does not require a ground truth to compare against.
## Gates, Experiments, and Analytics
The standard Statsig product capabilities are also available for use with AI Evals. For example, you can target an LLM feature at users who meet specific criteria using a Feature Gate, or roll out a new prompt version as an Experiment to understand its impact on metrics.
### LLM as a judge
Some grading can use heuristics (for example, checking if the AI-generated output matches the ideal answer when the output is as simple as High, Medium, or Low). Other grading can't use heuristics: for example, deciding whether "Your ticket has been escalated" and "This ticket has been escalated" mean the same thing. LLM-as-a-judge lets you evaluate AI outputs at scale without requiring many human reviewers. It mimics how a human assesses quality: not perfect, but fast, consistent, and useful for comparing different versions of your model or prompt. In the example above, you could write an LLM-as-a-judge prompt such as "Score how close this answer is to the ideal one on a scale of 0 to 1.0".
================================================================================
# Prompts & Graders
URL: https://docs.statsig.com/ai-evals/prompts
Updated: 2026-07-11
================================================================================
## What is a prompt in Statsig?
A Prompt represents an LLM prompt or task in Statsig, along with its config. Prompts are similar to Dynamic Configs and let you evaluate and roll out prompts in production without deploying code. Use the Statsig [Node](/server-core/node-core#getting-a-prompt) or [Python](/server-core/python-core/#getting-a-prompt) Server Core SDKs to retrieve a prompt within your app at runtime.
With Prompts, you can:
* Manage prompt configuration outside of your application code. You can update the model, configuration, or prompt at runtime.
* Allow teammates with access to Statsig to collaborate and iterate on prompts, benefiting from Statsig's production change control processes and versioning.
* Add configuration for a new model or model provider and progressively shift production traffic to it while comparing costs, user satisfaction, or any metric of interest.
* Support advanced use cases such as retrieval-augmented generation (RAG) and evaluation in production.
{% figure caption="Creating a prompt" %}

{% /figure %}
{% figure caption="Code snippet to retrieve the Live version of the prompt" %}

{% /figure %}
{% figure caption="Looking at the scores for a prompt version" %}

{% /figure %}
## What is a grader?
A grader is the evaluation component that scores or judges the output of an AI system against a target standard.
The grader is the core evaluation unit in the workflow:
* **Inputs:** The grader takes in the AI model’s response and, when available, the ideal or ground-truth answer.
* **Process:** The grader applies a scoring method. This can be rule-based (exact string match, regex check, cosine similarity) or LLM-as-a-judge (using another model to evaluate correctness, relevance, style, or safety).
* **Outputs:** The grader produces a score, ideally 0 (Fail) or 1 (Pass). This score feeds into the Statsig experiment or eval framework to determine performance across datasets, experiments, or model versions.
## What is a critical grader?
A critical grader is a must-pass evaluation in Statsig AI Evals. If the AI output fails this grader, the entire run is marked as failed. A critical grader enforces non-negotiable requirements, acting as a hard gate before results are considered valid. When it doesn't fail, it acts like a normal grader.
### Use Case
For example, in a financial support chatbot, a critical grader could check that the model never fabricates account balances. Even if the response is otherwise helpful, a single failure blocks the model from being promoted.
================================================================================
# Offline Evals
URL: https://docs.statsig.com/ai-evals/offline-evals
Updated: 2026-07-11
================================================================================
## What are offline evals
Offline evals provide quick, automated grading of model outputs on a fixed test set. They catch wins and regressions before any real users are exposed. For example, compare a new support bot's replies to human-curated answers to decide if the bot is ready to ship.
Steps to run offline evals on Statsig:
1. Create a Prompt that contains the instruction for your task (for example, "Classify tickets as high, medium, or low urgency based on ticket text").
2. Upload a sample dataset with example inputs and ideal answers (for example, Ticket1 text to High; Ticket2 text to Low).
3. Run your AI on the dataset to produce output (for example, classify each ticket).
4. Grade or score the outputs by comparing the ideal answers in the dataset with the AI-generated output.
5. Create multiple versions of your prompts, compare scores across versions, and promote the best one to Live.
## Create/analyze an offline eval in 10 minutes
**1. Create a Prompt within Statsig**
This captures the instruction you provide to an LLM to accomplish your task. Use the Statsig [Node](/server-core/node-core#getting-a-prompt) or [Python](/server-core/python-core/#getting-a-prompt) Server Core SDKs to retrieve this prompt within your app. You can create multiple versions of the prompt as you iterate and choose which one is "live" (retrieved by the SDK).

**2. Create a dataset you can use to evaluate LLM completions for your prompt**
For the example above, this might be a list of words alongside known good translations in French. Small lists can be entered manually, or you can upload a CSV.

**3. Create a grader that grades LLM completions for your prompt**
Configure a grader that compares the LLM completion text with the reference output. Use one of the built-in string evaluators, or configure an LLM-as-a-judge evaluator that mimics a human's grading rubric.

**3. Run evaluation**
Run an evaluation on a version of the prompt. You should see results in a few minutes that look like this. You can click into any row of the dataset to understand more about the evaluation for that row.

You can categorize your dataset, and break scores out by category.

If you have scores for multiple versions, you can compare them to see what changed between versions.

================================================================================
# Online Evals
URL: https://docs.statsig.com/ai-evals/online-evals
Updated: 2026-07-11
================================================================================
## What are online evals
Online evals let you grade model output in production on real-world use cases. You can run the "live" version of a prompt and also shadow-run "candidate" versions without exposing users to them. Grading works directly on the model output and doesn't require a ground truth to compare against.
Steps to run online evals in Statsig:
1. Create a Prompt that contains the instruction for your task (for example, "Summarize ticket content. Do not include email addresses or credit card numbers in the summary"). Create a v2 prompt that improves on this.
2. In your app, produce model output using the v1 and v2 prompts. The output from v1 is rendered to the user; the outputs from v1 and v2 are both judged by an LLM-as-a-judge.
3. Statsig logs the grades from v1 and v2 for comparison.
{% callout type="info" %}
Statsig isn't accepting new customers for AI Evals at this time.
{% /callout %}
## Create/analyze an online eval in 15 minutes
**1. Identify the prompts you want to serve**
In Prompts, there are four prompt types: Live, Candidate, Draft and Archive. Before starting an online evaluation, it’s important to organize your prompt versions into these categories:
* **Live** prompt is the version actively served to users.
* **Candidate** prompts aren’t shown to users but are still served to your code. Statsig still processes the user’s input against them and logs and grades their outputs alongside the live version.
* **Draft** prompts are the offline prompts you iterate on in the console, before deciding that you want to serve them. To start serving them, promote them to "Candidate" or "Live."
* **Archive** prompts are inactive versions that you don't iterate on, kept offline.
Prompts that you can access in code comprise the Live version and Candidate versions.

**2. Load your prompts in code and run completions on user input**
The example below shows how to integrate prompts in your application using the [Statsig AI SDKs](/ai-evals/node). After you retrieve your Live or Candidate prompts, pass in the appropriate values to replace the macros in your prompt (`{{input}}` should be replaced by the user input). Then run completions on each of these prompts.
```js
const prompts = statsigAI.getPrompt(user, "ai_config_name");
// get the live prompt
const livePrompt = prompts.getLive();
// get the candidate prompts
const candidatePrompts = prompts.getCandidates();
// get the live prompt messages
const livePromptMessages = livePrompt.getPromptMessages({ input: userInput });
// run completions on your live prompt and show the output to the user
const liveOutput = client.completions.create(
my_model,
livePromptMessages,
(temperature = livePrompt.getTemperature())
);
// simulateneously run completions on the candidate prompts to get their output
```
**3. Score your output using graders**
After you have a completion’s output, evaluate it using a grader: either one created in Statsig or a custom grader of your choice. The resulting score must always fall within the range of 0 to 1.
**4. Log eval results to Statsig**
Log your scores as events in Statsig to view the results in your console.
```js
// Log the results of the eval
statsigAI.logEvalGrade(user, livePromptVersion, 0.5, "my_grader", {
session_id: "1234567890",
});
```
**5. View results in Statsig**
View these results in Statsig. Select the version you want to evaluate and the versions to compare it against. This end-to-end online eval helps you iterate on your prompts and gain insights.

================================================================================
# Statsig Visual Editor (Low-code Experiments)
URL: https://docs.statsig.com/guides/sidecar-experiments/introduction
Updated: 2026-07-11
================================================================================
## How A/B testing works
A/B testing, or split testing, is a fundamental method in digital marketing for validating website changes. It involves comparing two versions of a webpage to see which performs better with a target audience.
Traditionally, implementing A/B tests required substantial technical skills, often requiring collaboration between marketers and developers, leading to longer implementation times and reduced agility for marketing teams.
## Statsig's visual editor
Statsig's Visual Editor lets marketers run A/B tests with minimal technical dependency. It provides an intuitive, point-and-click interface for setting up and managing A/B tests on your website. You can experiment on styling, text content, calls to action, and injected scripts that change page behavior.
Combined with Statsig's [industry-leading stats engine](/experiments/overview), Sidecar is a useful tool in a marketer's toolkit.
{% figure %}

{% /figure %}
{% callout type="info" %}
This guide assumes you have an existing Statsig account. Go here to create a new free account if you don't already have one: https://statsig.com/signup
{% /callout %}
## Performance considerations
Sidecar, like any client-side tooling, introduces some degree of page load latency. Test Sidecar and verify that the performance impact falls within your acceptable threshold.
Performance-sensitive customers typically use our [JS-SDK](/client/javascript-sdk) for web testing. When using the JS/React SDKs with [Client Bootstrapping](/client/concepts/initialize), the latency is minimal because no network requests are required for initialization.
================================================================================
# Visual Editor Setup & Usage
URL: https://docs.statsig.com/guides/sidecar-experiments/sidecar-v3
Updated: 2026-07-11
================================================================================
## How Visual Editor v3 works
Visual Editor v3 provides a simpler interface for point-and-shoot experiments. Unlike previous versions, you create experiments in the Statsig Console and edit them directly on top of the page. Sidecar still relies on a JavaScript script tag, though the package name has changed.
{% callout type="info" %}
Sidecar v3 is in Early Access. Try the product and share any feedback in the [Slack Community](https://statsig.com/slack).
{% /callout %}
## Prerequisites
### 1. Installing the Sidecar script
For experiments to take effect, the visual editor ("sidecar") script must run on your website, on any page you want to experiment on:
```html
```
Replace `client-key` with a client key from your Statsig project. You can find it at [Settings > Keys & Environments](https://console.statsig.com/api_keys).
Most website builders also support the ability to add script tags on your website, like:
[Webflow](https://university.webflow.com/lesson/custom-code-in-the-head-and-body-tags?topics=site-settings),
[Wordpress](https://wordpress.com/go/website-building/how-to-properly-add-javascript-to-wordpress-3-top-methods/),
[Wix](https://support.wix.com/en/article/embedding-custom-code-on-your-site),
[Squarespace](https://support.squarespace.com/hc/en-us/articles/205815928-Adding-custom-code-to-your-site),
[Weebly](https://weeblytutorials.com/embed-javascript-weebly).
### 2. Installing the Chrome extension (for editing)
To create edits for experiment variants, install the Statsig Visual Editor [Chrome Extension](https://chromewebstore.google.com/detail/statsig-sidecar-v3/mmgjfcbidnlghegclgpkgegpdhbopjhn).
You also need to be a project admin or have permissions to access console API keys to use all Sidecar functionality.
## Creating an experiment
Create experiments in the console by opening the Create Experiment dialog and changing the experiment type to "Visual Editor".

## Setting up an experiment
### Metrics
When you add the Statsig Visual Editor script to your website, Statsig automatically tracks events like clicks, page views, Core Web Vitals, and more. These are called [autocapture](/webanalytics/autocapture/) metrics. Go to [Metrics](/metrics/101) for more information.
You can add any of these metrics to your experiment, or customize them to be filtered to certain attributes (e.g. clicking a certain button, visiting a certain page) by creating a new metric in the [Metrics Catalog](https://console.statsig.com/metrics/metrics_catalog) tab.
You must add at least one metric to your experiment before continuing.
### URL filters
To limit an experiment to a subset of pages on your website, configure which URLs the experiment runs on:
* **All Pages**: anywhere the Sidecar client is installed
* **Contains**: The page URL must contain the value entered, for example "pricing"
* **Exact Match**: The page URL must match the exact value specified here.
* **Regex**: Regular expressions, for example `(http|https):\/\/www.statsig.com\/pricing` matches pages `http://www.statsig.com/pricing` or \`https://www.statsig.com/

### Audiences
Statsig infers information about each user, including country, device type, browser type, and a unique identifier called StableID. You can target based on any of these attributes using the "audiences" targeting section. You can also target custom attributes added to the Window.statsigUser object, though this requires a small amount of code.
### Visual Editor starting URL
Before editing experiment variants, add a Visual Editor URL. The editor opens at this URL when you begin editing. Add a URL starting with "https://", then click **Save**.
To set up an experiment, enter your metrics and any targeting (on URL, or other user attributes per the [StatsigUser object](/sdks/user)), and the starting URL. The URL must begin with "https://". Click **Save**, then click **Open in Editor**. An editor bar appears along the bottom of the page:

### Runtime settings
For more advanced experiment flows, you can also configure runtime settings on the experiment setup page before opening the editor.
* **Disable Auto Run** prevents the experiment from applying automatically on page load.
* **Prerun Script** lets you run custom JavaScript before the experiment starts. This is useful for SPAs or other advanced flows where you need to bind listeners or evaluate custom logic before manually activating the experiment with `StatsigSidecar.activateExperiment("experiment_name")`.
Go to [Advanced Configurations](/guides/sidecar-experiments/advanced-configurations-v3) for more detail on runtime settings, manual activation, and prerun scripts.
## Making your edits
Click **Element selector** or press command + E to enable the element editor. The editor provides a preset set of editable attributes depending on the selected element, for example:
* *Text:* Text, Font attributes
* *Button:* Text, target link
* *Image:* Image source
All elements also support rearranging or hiding attributes.

## Starting your experiment
Click **Save** in the bottom-right of the visual editor to save changes to the console. Return to the console page and refresh to see your changes listed. Preview the experiment by clicking the three dots in the variant table. When ready, launch the experiment by clicking **Start** in the top-right corner. After starting, the experiment appears for end users and Statsig collects metrics.
### Advanced script setup
Add these query string parameters to the Sidecar script URL for additional controls over Sidecar client behavior.
* `&reduceflicker=0` disables the brief hiding of the `` tag while the client initializes.
## Advanced configurations
For more advanced Sidecar controls after the basic setup is working:
* Go to [Advanced Configurations](/guides/sidecar-experiments/advanced-configurations-v3) for runtime settings, manual activation, prerun scripts, script URL tuning, consent, and identity configuration.
* Go to [Measuring Experiments](/guides/sidecar-experiments/measuring-experiments#per-assignment-callback-for-outbound-integrations) for outbound analytics callbacks and other measurement patterns.
================================================================================
# Measuring Experiments
URL: https://docs.statsig.com/guides/sidecar-experiments/measuring-experiments
Updated: 2026-07-11
================================================================================
## Using Autocapture
Sidecar automatically tracks various web activities, letting you create simple and complex metrics in the Statsig console without writing code. Create a new metric in the Metrics tab on the Statsig console to get started. For a full list of automatically logged metrics, refer to [Autocapture on the Web](/webanalytics/autocapture).
## Using the tracking API
You can track events manually for actions not captured by Autocapture.
To track events to Statsig, call `StatsigSidecar.logEvent`, which accepts the same arguments as the Statsig JS SDK as documented in [Logging an event](/client/javascript-sdk#logging-an-event). You can call this method before the init routine completes.
```js
// example order event
StatsigSidecar.logEvent('Order', null, {
total: 54.66,
units: 3,
unitAvgCost: 18.22
});
```
## Per-assignment callback for outbound integrations
You can bind a callback that is invoked each time Sidecar activates an experiment assignment, including experiments activated later by prerun scripts.
*This method must be defined before the Sidecar client script.*
```js
window.statsigSidecarConfig = {
onExperimentEvaluation: function (event) {
/**
* add your own callback routine here
* ie; annotating 3rd party analytics tools with assignment info
*/
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: "statsig_experiment_evaluation",
experiment_name: event.experimentName,
experiment_group_name: event.groupName,
});
}
}
```
The callback payload includes:
* `event.name` - always `"experiment_evaluation"`
* `event.experiment` - the full Statsig experiment object
* `event.experimentName` - the Sidecar experiment name, or the Statsig experiment name as a fallback
* `event.groupName` - the assigned group / variant, or `null` if unavailable
## Post-experiment callback for one-time readiness hooks
You can bind `window.postExperimentCallback` to receive a callback after Sidecar finishes its initial run. This callback fires even when there are no experiments, but doesn't cover experiments activated later by prerun scripts.
```js
window.postExperimentCallback = function(statsigClient, experimentIds) {
// One-time initialization hook after Sidecar finishes the initial run
}
```
### Disabling all logging
To disable all logging to Statsig (both autocapture events and experiment exposure logging), append the following query string parameter to the Sidecar script URL: `&autostart=0`. This is useful for GDPR compliance. You can re-enable events later with `client.updateRuntimeOptions({disableLogging: false})`.
================================================================================
# Advanced Configurations
URL: https://docs.statsig.com/guides/sidecar-experiments/advanced-configurations-v3
Updated: 2026-07-11
================================================================================
## Runtime settings for SPAs and manual activation
Visual Editor v3 includes runtime settings that let you control when an experiment starts running on the page. Configure these settings on the experiment setup page in the Statsig Console.
* **Disable Auto Run** prevents Sidecar from automatically attempting to run the experiment on page load.
* **Custom activation** lets you start the experiment manually with `StatsigSidecar.activateExperiment("experiment_name")`.
* **Prerun Script** lets you define a custom script that runs once before the experiment starts when the targeting rules pass. Use this to bind listeners or evaluate custom logic before manually activating the experiment.
## Advanced targeting and segmentation
This section describes how to set user identity and attributes for Sidecar to enable more advanced targeting and results segmentation.
By default, Sidecar and Autocapture use `stableID` (an auto-generated device-level GUID stored in the user's localStorage) for tracking. To enrich autocapture events with known user identities and attributes, define the following object before autocapture or Sidecar loads.
Watch this [video tutorial](https://tinyurl.com/sidecar-targeting) on how to configure more advanced targeting for your Sidecar experiments.
```js
window.statsigUser = {
userID: "",
custom: { // optional attributes object
isLoggedIn: false
}
}
```
## Accessing the Statsig JS client
To access the underlying Statsig js client instance, call `StatsigSidecar.getStatsigInstance()`.
## Configuring runtime options
Use runtime options to handle consent management, GDPR compliance, and more. All [StatsigOptions](/client/javascript-sdk/#statsig-options) provided by the JavaScript SDK are fully supported with Sidecar. Pass them to Sidecar using:
```js
window.statsigOptions = {
// example of disabling logging for
loggingEnabled: 'disabled'
}
```
## Managing Consent
Before the Sidecar script tag, configure these runtime options to disable browser storage and tracking:
```js
window.statsigOptions = {
loggingEnabled: "disabled",
disableStorage: true
}
```
After the user gives consent, re-enable storage and tracking:
```js
__STATSIG__.instance().updateRuntimeOptions({loggingEnabled: "browser-only", disableStorage: false});
```
## Persisting stableID across subdomains
Statsig uses `localStorage` as the preferred mechanism for storing the user's stableID. localStorage keys don't persist across origin boundaries, including subdomains. For example, a user visiting `https://example.com`, `https://show.example.com`, and `https://account.example.com` receives three distinct stableIDs.
If you assign a user to a test on one subdomain and track behavior for metrics on a different subdomain, add the following solution to ensure Statsig correctly attributes cross-origin behavior to the Test Group assignment from the initial experiment domain.
Paste the following in your HEAD section.
```html
```
================================================================================
# Setting up Sidecar
URL: https://docs.statsig.com/guides/sidecar-experiments/setup
Updated: 2026-07-11
================================================================================
{% callout type="info" %}
* This guide assumes you have an existing Statsig account. Go here to create a new free account if you don't already have one: https://statsig.com/signup
* You need to use the Google Chrome web browser for this exercise.
{% /callout %}
## Set up Sidecar Chrome extension
### Step 1: Install Chrome extension
If you don't already have the Sidecar extension, visit the [Chrome Web Store](https://chromewebstore.google.com/detail/statsig-sidecar/blkgemeefnlkmicphlkodgdkhceibgcb) and select "Add to Chrome".
{% figure %}

{% /figure %}
### Step 2: Activate the extension
Select the Extensions toolbar button and choose "Statsig Sidecar" to activate the Sidecar extension.
{% figure %}

{% /figure %}
You now see an Experiment Config UI like this:
{% figure %}

{% /figure %}
### Step 3: Update settings
Update the API keys in the Settings dialog for the extension to work. Open the Settings dialog from the "Settings" link in the top header.
{% figure %}

{% /figure %}
Retrieve these keys from your Statsig project. Log in to the Statsig Console at https://console.statsig.com and navigate to Settings (https://console.statsig.com/settings).
Select the "Keys & Environments" panel, then copy both the Console API Key and Client API Key and paste them into the Settings dialog.
{% figure %}

{% /figure %}
Select "OK" to save the API Keys.
## Install Sidecar on your website
Add a single script tag within the `` portion of your website, replacing with your own [Client SDK Key](/access-management/api-keys) as shown below.
```
```
{% callout type="info" %}
Installing Sidecar JS through a Tag Manager can lead to flickering and other unpredictable behavior. Install Sidecar as a synchronous script tag instead.
{% /callout %}
### Additional Options
Add these query string parameters to the Sidecar script URL for additional controls over Sidecar client behavior
* `&reduceflicker=0` will disable the brief hiding of the `` tag while the client initializes
* `&autocapture=0` will disable event autocapture
Most website builders support adding script tags. Here are some common examples:
[Webflow](https://university.webflow.com/lesson/custom-code-in-the-head-and-body-tags?topics=site-settings), [Wordpress](https://wordpress.com/go/website-building/how-to-properly-add-javascript-to-wordpress-3-top-methods/), [Wix](https://support.wix.com/en/article/embedding-custom-code-on-your-site), [Squarespace](https://support.squarespace.com/hc/en-us/articles/205815928-Adding-custom-code-to-your-site), [Weebly](https://weeblytutorials.com/embed-javascript-weebly).
You can copy the script code from within the Sidecar Chrome extension.
{% figure %}

{% /figure %}
You are now ready to create your first experiment.
## Next up: [Creating Experiments](/guides/sidecar-experiments/creating-experiments)
================================================================================
# Creating Your First Experiment
URL: https://docs.statsig.com/guides/sidecar-experiments/creating-experiments
Updated: 2026-07-11
================================================================================
Sidecar lets you create and run A/B experiments without writing code or pushing code to production. This guide shows how to create an experiment and review results.
{% callout type="info" %}
This guide assumes you have followed the previous steps of installing Sidecar, creating a Statsig account, and setting up the API Keys correctly. Refer to [Setup](/guides/sidecar-experiments/setup) for those instructions.
{% /callout %}
## Step 1: Navigate to the web page
Navigate to the web page you want to experiment on.
{% figure %}

{% /figure %}
## Step 2: New experiment
Select the *New Experiment* button and fill out the details. This creates a local experiment that has not been published yet, so you can configure all the details and verify that everything works before publishing.
{% figure %}

{% /figure %}
## Step 3 (Optional): Add URL filter
You can select which pages the experiment runs on. URL filters are evaluated before any targeting rules configured on the experiment in the Statsig console.
You can configure URL targeting using the following methods:
* All Pages - anywhere Sidecar client is installed
* Contains - The page URL must contain the value as a substring
* Exact Match - The page URL must match the exact value specified here.
* Regex - Regular expressions, for example `(http|https):\/\/www.statsig.com\/pricing` matches pages `http://www.statsig.com/pricing` or `https://www.statsig.com/pricing`, and will activate this experiment on those pages.
{% figure %}

{% /figure %}
## Step 4: Add actions
Select the *Add Action* button to see a list of actions available for this experiment.
Choose *Change content of an element* to run an A/B test that changes the content of an HTML element, such as headlines, descriptions, or CTAs.
{% figure %}

{% /figure %}
### Use the Redirect action for landing page and split URL experiments
For Landing Page and Split URL experiments, add the "Redirect to another page" action for any test group and specify the destination URL. Query string parameters are preserved and passed to the destination URL.
{% figure %}

{% /figure %}
## Step 5: Select an element
To run a content change experiment, you need two things: the element to test, and the content to change.
Select the yellow *Target element path* text box to activate element selector mode.
Move your mouse over the web page to see a red selection rectangle. Select the element you want. In this example, the main Headline is selected.
{% figure %}

{% /figure %}
Sidecar now reflects the path of the selected element.
{% figure %}

{% /figure %}
## Step 6: Update content
Choose the two text values to A/B test. In the *Control content* text box, add your control text (for example, "Build Better Products"). In the *Test content* text box, add your test variant (for example, "Experiment Like a Pro").
You can validate these changes in real time by selecting the ▶ button above the text box for each variant. This immediately changes the element's content so you can visually inspect the result before publishing.
{% figure %}

{% /figure %}
## Step 7: Add more actions
You can add more actions within the same experiment to explore additional capabilities.
{% figure %}

{% /figure %}
You have created your first no-code experiment.
## Next up: [Measuring Experiments](/guides/sidecar-experiments/measuring-experiments)
================================================================================
# Advanced Configurations
URL: https://docs.statsig.com/guides/sidecar-experiments/advanced-configurations
Updated: 2026-07-11
================================================================================
## Single Page App support
Sidecar supports integration within Single Page Apps. This includes Sidecar configuration tools and SDK methods for defining custom trigger points, giving you full flexibility to control when an experiment runs.
* **Disable Auto Run**: When selected, Sidecar doesn't automatically attempt to run the test on page load.
* **Custom activation**: Use `StatsigSidecar.activateExperiment("")` to activate an experiment manually.
* **Prerun Script**: Define a custom script that runs only once per experiment if the URL filter passes. Use this to bind listeners or evaluate custom logic to control when to trigger the experiment using `activateExperiment`.
{% figure %}

{% /figure %}
## Advanced targeting and segmentation
This section describes how to set user identity and attributes for Sidecar to enable more advanced targeting and results segmentation.
By default, Sidecar and Autocapture use `stableID` (an auto-generated device-level GUID stored in the user's localStorage) for tracking. To enrich autocapture events with known user identities and attributes, define the following object before autocapture or Sidecar loads.
Watch this [video tutorial](https://tinyurl.com/sidecar-targeting) on how to configure more advanced targeting for your Sidecar experiments.
```js
window.statsigUser = {
userID: "",
custom: { // optional attributes object
isLoggedIn: false
}
}
```
## Accessing the Statsig JS client
To access the underlying Statsig js client instance, call `StatsigSidecar.getStatsigInstance()`.
## Configuring runtime options
Use runtime options to handle consent management, GDPR compliance, and more. All [StatsigOptions](/client/javascript-sdk/#statsig-options) provided by the JavaScript SDK are fully supported with Sidecar. Pass them to Sidecar using:
```js
window.statsigOptions = {
// example of disabling logging for
loggingEnabled: 'disabled'
}
```
## Managing Consent
Before the Sidecar script tag, configure these runtime options to disable browser storage and tracking:
```js
window.statsigOptions = {
loggingEnabled: "disabled",
disableStorage: true
}
```
After the user gives consent, re-enable storage and tracking:
```js
__STATSIG__.instance().updateRuntimeOptions({loggingEnabled: "browser-only", disableStorage: false});
```
## Persisting stableID across subdomains
Statsig uses `localStorage` as the preferred mechanism for storing the user's stableID. localStorage keys don't persist across origin boundaries, including subdomains. For example, a user visiting `https://example.com`, `https://show.example.com`, and `https://account.example.com` receives three distinct stableIDs.
If you assign a user to a test on one subdomain and track behavior for metrics on a different subdomain, add the following solution to ensure Statsig correctly attributes cross-origin behavior to the Test Group assignment from the initial experiment domain.
Paste the following in your HEAD section.
```html
```
================================================================================
# Taking your experiments to production
URL: https://docs.statsig.com/guides/sidecar-experiments/publishing-experiments
Updated: 2026-07-11
================================================================================
After completing the experiment configuration, publish and launch it in production. Sidecar makes this process straightforward with a few steps.
{% callout type="info" %}
This guide assumes you have followed the previous steps of creating an experiment in Sidecar. Check out [Creating Experiments](/guides/sidecar-experiments/creating-experiments) for those instructions.
{% /callout %}
## Step 1: Publish the experiments
When you are satisfied with the experiment configuration, select the blue _Publish_ button. This saves all configurations to Statsig. To verify the changes were stored successfully, select the `...` menu and choose _Go to Experiment Console_.
Publishing changes doesn't start any experiments; it does the following:
- Sync any unsaved changes to Statsig (making them accessible in *Console* where you can configure metrics and other targeting conditions if applicable).
- Include any configured tests in the Sidecar script installed on your website.
- Allow you to QA experiments on your site while they're in an unstarted state.
{% figure %}

{% /figure %}
The experiment console lets you configure targeting, metrics, and advanced statistical settings.
{% figure %}
{% /figure %}
## Step 2: Preview and QA the experiment
At this point, your experiment is in a pre-started state, meaning it isn't yet active to site visitors.
To preview a specific variant, pass a query string to your test page URL using the `overrideuser` parameter.
The override method uses the following convention to force a test & test group:
`https://www.DOMAIN.com/?overrideuser=_`
The image below shows where to find the experiment ID and each variation ID. Based on this example, force a preview of the Test Group by visiting:
`https://www.DOMAIN.com/?overrideuser=name_color_test_1`
{% figure %}

{% /figure %}
{% callout type="note" %}
This works best with the default test/control group names. If you change a group name, you must also update it in the Statsig Console by selecting "Manage Overrides".
{% /callout %}
## Step 3: Start the experiment
Refresh the page in your browser with the script embedded. Sidecar automatically detects the published experiment and displays all experiment properties.
Start the experiment by selecting the `...` menu and choosing _Start Experiment_. Sidecar then starts the experiment, serves the correct variants for control and test groups, and begins collecting metrics.
{% figure %}

{% /figure %}
You have successfully built and shipped an experiment.
================================================================================
# Integrating Sidecar with GTM for tracking
URL: https://docs.statsig.com/guides/sidecar-experiments/integrating-gtm
Updated: 2026-07-11
================================================================================
{% callout type="info" %}
This integration is for tracking purposes only. Avoid loading Sidecar itself through GTM, as this delays the changes from being applied and results in "flickering".
{% /callout %}
## How GTM integration works
This integration automatically sends GTM-tagged events to Statsig. No additional coding or tagging is required after completing these steps.
{% figure %}

{% /figure %}
*(statsig logstream showing gtm events flowing in)*
## Step 1: Create new tag
{% figure %}

{% /figure %}
## Step 2: Choose tag type
Choose "Custom HTML" for tag type, and paste [this GTM code](#gtm-code) (including script tag)
{% figure %}

{% /figure %}
## Step 3: Adjust fire options
Under Advanced Settings under "Tag Firing options", select "Once per page"
{% figure %}

{% /figure %}
## Step 4: Set tag trigger
Below the "Tag Configuration" section, set the Trigger to "Initialization - All Pages" Option.
{% figure %}

{% /figure %}
## Step 5: Save tag and test
After saving and publishing your updated GTM tag, tracking runs automatically without any additional configuration.
To debug the integration, set a local storage entry `debug_ss_gtm` with any value on your webpage. This produces console log statements for each tracking call dispatched to Statsig. You can also inspect your browser's network traffic to see events being tracked.
## GTM code
```html
```
================================================================================
# Running an A/A Test using Sidecar
URL: https://docs.statsig.com/guides/aa-sidecar
Updated: 2026-07-11
================================================================================
This guide explains how to use Statsig’s Sidecar to run an A/A test on your product.
{% callout type=”info” %}
This guide assumes you have successfully set up and configured Statsig Sidecar. For a step-by-step guide, go to [“Setting up Sidecar”](/guides/sidecar-experiments/setup).
{% /callout %}
## Why run an A/A test?
The most common reason to run an A/A test is to validate a new experimentation engine you're integrating with, such as Statsig. For new users, running an A/A test first provides a low-stakes environment to confirm that your metrics are set up correctly and that exposures are flowing through as expected before you start your first real A/B test.
## How to run an A/A test
### Step 1: Create a new Experiment in Sidecar
Navigate to the page on your website where you want to run an A/A Test. Open the Statsig Sidecar extension and select **New Experiment**. Fill in the title of your A/A test.
{% figure %}

{% /figure %}
Then, determine the URI filter (for example, All Pages, contains). After you configure the URI, set up the variants.
For the variant 'Control', choose an action. In this example, the action changes the content of an element: specifically the title 'Getting Started is Simple'.
{% figure %}

{% /figure %}
Repeat the step above for the variant 'Test', using the exact same action. Your setup should match the following.
{% figure %}

{% /figure %}
Select **Publish** to push the experiment to Statsig as a draft.
### Step 2: Configure experiment scorecard in Statsig console
After Sidecar pushes the experiment to end users, edit the scorecard for your experiment in the console. Navigate to the console, select the **Experiments** tab, and open the experiment you just created.
In the **Setup** tab, fill out the scorecard with the experiment hypothesis and any primary metrics you want to monitor. Statsig shows results for all your metrics, but these key metrics represent your hypothesis. Establishing a hypothesis upfront ensures the experiment improves your understanding of users, rather than providing data points to support a predetermined decision.
In the Allocation and Targeting section, for an A/A test, allocate 100% of users to the experiment while targeting everyone.
Save and push your experiment. Your test is now ready to start measuring metrics associated with the A/A test.
### Step 3: Review A/A test results
Within 24 hours of starting your experiment, cumulative exposures appear in the **Pulse Results** tab of your experiment.
This shows a breakdown of your logged exposures and their distribution. If something looks off, check the **Diagnostics** tab for more granular, day-by-day exposure breakdowns at both the Checks and User level.
In the **Scorecard** panel, you can see the full picture of how all your tagged metrics perform.
{% figure %}

{% /figure %}
What to expect:
* **Exposures:** Confirm that exposures are flowing through as expected from your product. If exposures are missing, use the **Diagnostics** tab and the **Exposure Stream** to debug.
* **Pulse results:** About 5% of your metrics in Pulse should show a statistically significant change, because Statsig’s stats engine uses a 95% confidence interval.
Run your A/A test long enough to reach most of your weekly active users, or at least one week.
================================================================================
# Session Replay Overview
URL: https://docs.statsig.com/session-replay/overview
Updated: 2026-07-11
================================================================================
Session Replay records users interacting with your website or product and lets you play back those sessions. Use it to understand how users navigate your service, diagnose problems, and uncover insights that improve conversion and the overall user experience.
A session recording plays back like a video in the Statsig Console. It is a serialized representation of your website and all events and interactions that occurred during the session. Statsig captures recordings using the [rrweb open source recording library](https://github.com/rrweb-io/rrweb). This approach is performant and space-efficient, with options to apply privacy filters to on-screen content.
{% figure %}

{% /figure %}
================================================================================
# Install Statsig Session Replay
URL: https://docs.statsig.com/session-replay/install
Updated: 2026-07-11
================================================================================
Statsig supports Session Replay on the Javascript and React SDKs for both desktop and mobile web users. Follow the instructions below to install the SDK and record user sessions.
## Option 1 - No code - Add Javascript snippet to your website
```html
```
Get YOUR\_CLIENT\_KEY from *Project Settings > Keys & Environments*. Reveal the Client API Key, copy, and paste it over the \[YOUR-API-KEY] in the snippet above.
This auto-initializes the SDK and starts recording sessions, no code required.
{% callout type="info" %}
To use your existing Statsig integration or customize further, refer to option 2 below. To use the script tag with customization, remove your key from the script URL and initialize using the JavaScript code below.
{% /callout %}
## Option 2 - Custom code - Install using a package manager
{% tabs %}
{% tab title="Javascript" %}
{% codetabs %}
```bash npm
npm install @statsig/js-client @statsig/session-replay @statsig/web-analytics
```
```bash yarn
yarn add @statsig/js-client @statsig/session-replay @statsig/web-analytics
```
{% /codetabs %}
{% /tab %}
{% tab title="React" %}
{% codetabs %}
```bash npm
npm install @statsig/session-replay @statsig/web-analytics @statsig/react-bindings
```
```bash yarn
yarn add @statsig/session-replay @statsig/web-analytics @statsig/react-bindings
```
{% /codetabs %}
{% /tab %}
{% /tabs %}
Statsig recommends Autocapture for getting started quickly. To skip automatic event logging, remove the `runStatsigAutoCapture` option from the JavaScript snippet or skip the `@statsig/web-analytics` package installation.
Next, following the [instructions for the Statsig Javascript SDK](/client/javascript-sdk), initialize Statsig with your SDK key, [user](/sdks/user) and options:
{% tabs %}
{% tab title="Javascript" %}
```jsx
import { StatsigClient } from "@statsig/js-client";
import { runStatsigSessionReplay } from "@statsig/session-replay";
import { runStatsigAutoCapture } from "@statsig/web-analytics";
const client = new StatsigClient(
sdkKey,
{ userID: "some_user_id" },
{ environment: { tier: "production" } } // optional, pass options here if needed
);
runStatsigSessionReplay(client);
runStatsigAutoCapture(client);
await client.initializeAsync();
```
{% /tab %}
{% tab title="React" %}
```jsx
import { StatsigProvider, useClientAsyncInit } from "@statsig/react-bindings";
import { StatsigSessionReplayPlugin } from "@statsig/session-replay";
import { StatsigAutoCapturePlugin } from "@statsig/web-analytics";
function App() {
return (
Loading...}
options={{
plugins: [
new StatsigSessionReplayPlugin(),
new StatsigAutoCapturePlugin(),
],
}}
>
);
}
```
{% /tab %}
{% /tabs %}
{% callout type="info" %}
To use Conditional Triggers, you must use `StatsigTriggeredSessionReplay`. Refer to Configure (next page) for more information.
{% /callout %}
Continue to Configure to learn more about controlling who, what, and when you record sessions.
================================================================================
# Configure Statsig Session Replay
URL: https://docs.statsig.com/session-replay/configure
Updated: 2026-07-11
================================================================================
## Conditional recording
In the Statsig Console, you can configure your Session Replay settings under **Project Settings → Analytics & Session Replay**. You must be a project admin to modify these settings.
### Global targeting gate
The Global Targeting Gate controls who is *eligible* for session recording. If a user doesn't pass this gate, Statsig never records their sessions. By default, this is set to Everyone, meaning there are no restrictions and Statsig can record anyone. This gate defines the upper bound of session recording eligibility.
### Global sampling rate
The Global Sampling Rate determines what percentage of eligible sessions Statsig records from the start. By default, this is set to **100%**, meaning Statsig records all eligible sessions. You can lower this value to limit session recordings while still capturing a consistent percentage of sessions. This rate applies only to sessions that begin at session start and doesn't affect conditional triggers.
### Conditional triggers: events and exposures
Conditional triggers can start a session recording mid-session, even if recording didn't begin at session start. These triggers respect the Global Targeting Gate but operate independently of the Global Sampling Rate. When a trigger fires, the recording includes the last 30 seconds leading up to the event, if you enable the rolling window.
Types of conditional triggers:
* **Individual Gate Exposures**: Trigger based on exposure to a specific gate, optionally filtered by group (for example, Pass/Fail).
* **All Gate Exposures**: Trigger based on exposure to any gate, optionally filtered by group (for example, Pass/Fail). Individual Gate Exposure triggers override All Gate configuration.
* **Individual Experiment Exposures**: Trigger based on exposure to a specific experiment, optionally filtered by group (for example, Test/Control).
* **All Experiment Exposures**: Trigger based on exposure to any experiment, filtered by groups Test/Control. Statsig doesn't support other group names. If an experiment includes additional groups, configure individual triggers for each one. Individual Experiment Exposure triggers override All Experiment configuration.
* **Events**: Trigger based on a specific logged event, optionally filtered by event values (for example, "purchase\_event" with value "book").
For each trigger, you can define an individual sampling rate. Statsig evaluates this rate based on `session_id`, so the result (pass or fail) remains consistent for the same session, even if the trigger occurs multiple times.
**All Gates** and **All Experiments** conditional triggers are only available in `3.30.1` or higher.
If a conditional trigger occurs while a session recording is already in progress, the recording continues uninterrupted.


### Example walkthrough
Suppose you have the following setup (refer to the image above):
* The Global Targeting Gate `session_replay_global_targeting_gate` allows all US users and excludes everyone else.
* The Global Sampling Rate is set to 25%, so Statsig records only 25% of eligible US user sessions from the start.
* For the remaining 75% of eligible users, session recording can still begin mid-session if a conditional trigger occurs.
Example Scenario:
1. A US user starts a session. They don't pass the 25% Global Sampling Rate, so Statsig doesn't record their session from the beginning.
2. Later, a `purchase_event` occurs with value `book`. This event is set up as a conditional trigger with a 50% sampling rate. If this session fails the sampling rate check, recording doesn't start.
3. A minute later, the user encounters the `cool_new_feature` gate, and the recording begins.
{% callout type="note" %}
A trigger's sampling rate is consistent for the entire session based on
session\_id. So if `purchase_event` fails the sampling rate once, future
occurrences of the same event in that session will also fail.
{% /callout %}
### Initialization: StatsigTriggeredSessionReplay
{% tabs %}
{% tab title="Javascript" %}
```jsx
import { StatsigClient } from "@statsig/js-client";
import { runStatsigTriggeredSessionReplay } from "@statsig/session-replay";
import { runStatsigAutoCapture } from "@statsig/web-analytics";
const client = new StatsigClient(
sdkKey,
{ userID: "some_user_id" },
{ environment: { tier: "production" } } // optional, pass options here if needed
);
runStatsigTriggeredSessionReplay(client, {
autoStartRecording: true,
keepRollingWindow: true,
});
runStatsigAutoCapture(client);
await client.initializeAsync();
```
{% /tab %}
{% tab title="React" %}
```jsx
import { StatsigProvider, useClientAsyncInit } from "@statsig/react-bindings";
import { StatsigTriggeredSessionReplayPlugin } from "@statsig/session-replay";
import { StatsigAutoCapturePlugin } from "@statsig/web-analytics";
function App() {
return (
Loading...}
options={{
plugins: [
new StatsigTriggeredSessionReplayPlugin({
autoStartRecording: true,
keepRollingWindow: true,
}),
new StatsigAutoCapturePlugin(),
],
}}
>
);
}
```
{% /tab %}
{% /tabs %}
#### Initialization options
* `autoStartRecording`
* `true`: Recording *can* start automatically after initialization. Statsig respects the global targeting gate and sample rate.
* `false`: You *must* manually start recording using startRecording(). This is helpful if you want to start the recording after a set point and block any auto-recording before then.
* `keepRollingWindow`
* `true`: Statsig maintains a local rolling window of the last 30 seconds of the session, allowing recordings to include context leading up to a trigger.
* `false`: If a conditional trigger occurs, recording begins from that moment onward, with no historical context.
{% callout type="warning" %}
If you use bootstrapping, contact the Statsig team to confirm
your server SDK supports conditional recording.
{% /callout %}
## Advanced: forcing a recording on demand
To manually start a recording, use the `startRecording` API, which begins recording immediately when called.
* `startRecording`: Respects both the Global Targeting Gate and Global Sampling Rate. Use this if you want to start recordings after a certain point (for example, after login) while still applying the Global Sampling Rate.
* `forceStartRecording`: Respects the Global Targeting Gate but ignores the Global Sampling Rate. Useful for debugging or when you want to bypass the Global Sampling Rate.
* `stopRecording`: Stops the current recording, if one is in progress. Calling this method when no recording is active has no adverse effects. After you call stopRecording, conditional recording triggers don't automatically restart the recording. Only an explicit call to `startRecording` or `forceStartRecording` resumes recording.
If you have access to your Session Replay client, you can call these functions directly on the client instance.
```
const sessionReplayClient = new SessionReplay(client);
…
if (someCondition) {
sessionReplayClient.startRecording();
}
```
If you don't have access to the client instance, you can import the function from `@Statsig/session-replay` and call it using your SDK key.
```
import { startRecording } from '@Statsig/session-replay';
…
startRecording(CLIENT_SDK_KEY)
```
## Additional options
These are options provided by the rrweb recorder (the open source recording tool used internally)
| key | default | description |
| ------------------------ | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| blockClass | 'rr-block' | Use a string or RegExp to configure which elements should be blocked |
| blockSelector | null | Use a string to configure which selector should be blocked |
| ignoreClass | 'rr-ignore' | Use a string or RegExp to configure which elements should be ignored |
| ignoreSelector | null | Use a string to configure which selector should be ignored |
| ignoreCSSAttributes | null | array of CSS attributes that should be ignored |
| maskTextClass | 'rr-mask' | Use a string or RegExp to configure which elements should be masked |
| maskTextSelector | null | Use a string to configure which selector should be masked |
| maskAllInputs | false | mask all input content as \* |
| maskInputOptions | `{ password: true }` | mask some kinds of input \*. Refer to the [list](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L77-L95) |
| maskInputFn | - | customize mask input content recording logic |
| maskTextFn | - | customize mask text content recording logic |
| slimDOMOptions | `{}` | remove unnecessary parts of the DOM. Refer to the [list](https://github.com/rrweb-io/rrweb/blob/588164aa12f1d94576f89ae0210b98f6e971c895/packages/rrweb-snapshot/src/types.ts#L97-L108) |
| dataURLOptions | `{}` | Canvas image format and quality ,This parameter will be passed to the OffscreenCanvas.convertToBlob(),Using this parameter effectively reduces the size of the recorded data |
| inlineStylesheet | true | whether to inline the stylesheet in the events |
| hooks | `{}` | hooks for events. Refer to the [list](https://github.com/rrweb-io/rrweb/blob/9488deb6d54a5f04350c063d942da5e96ab74075/src/types.ts#L207) |
| packFn | - | refer to the [storage optimization recipe](https://github.com/rrweb-io/rrweb/blob/master/docs/recipes/optimize-storage.md) |
| sampling | - | refer to the [storage optimization recipe](https://github.com/rrweb-io/rrweb/blob/master/docs/recipes/optimize-storage.md) |
| recordCanvas | false | Whether to record the canvas element. Available options: `false`, `true` |
| recordCrossOriginIframes | false | Whether to record cross origin iframes. rrweb has to be injected in each child iframe for this to work. Available options: `false`, `true` |
| recordAfter | 'load' | If the document is not ready, then the recorder will start recording after the specified event is fired. Available options: `DOMContentLoaded`, `load` |
| inlineImages | false | whether to record the image content |
| collectFonts | false | whether to collect fonts in the website |
| userTriggeredOnInput | false | whether to add `userTriggered` on input events that indicates if this event was triggered directly by the user or not. [What is `userTriggered`?](https://github.com/rrweb-io/rrweb/pull/495) |
| plugins | \[] | load plugins to provide extended record functions. [What is plugins?](https://github.com/rrweb-io/rrweb/blob/master/docs/recipes/plugin.md) |
| errorHandler | - | A callback that is called if something inside of rrweb throws an error. The callback receives the error as argument. |
## Limits
### 4 hours per session or 30 min inactive time
Sessions end after four hours total or if the user returns from inactive time greater than 30 minutes later.
### Recording limits
| Tier | Monthly Limit | Daily Limit | Hourly Limit |
| :--------- | ------------: | ----------: | -----------: |
| Free | 50,000 | 3,500 | 3,500 |
| Pro | 100,000 | 7,000 | 7,000 |
| Enterprise | 100,000 | 7,000 | 7,000 |
After you reach this limit, the SDK automatically prevents new recordings from starting. You can monitor your session replay usage in your project settings. [Contact us](https://statsig.com/contact/demo) for custom contracts.
### Replay availability time
Statsig takes about 1 hour to process a recorded session before it appears in your console.
### Default 30 day retention
Sessions have a default retention period of 30 days and Statsig automatically deletes them after that time. You can configure a shorter retention period in settings if needed. Reducing your retention period doesn't affect your monthly session replay limit and is typically done for privacy and compliance purposes.
================================================================================
# Privacy Options for Session Replay
URL: https://docs.statsig.com/session-replay/privacy
Updated: 2026-07-11
================================================================================
To support your app’s privacy requirements and align with your organization’s policies, Statsig provides multiple ways to control replay privacy:
* **Baseline privacy options** - Select from three preset privacy configurations. Each option applies a different level of text masking, helping you protect PII and sensitive data according to your app’s and users’ needs
* **Fine-grained privacy controls** - After choosing a baseline configuration, use CSS selector rules to mask, reveal, or block specific elements
* **Global Targeting Gate** - Use a feature gate to define which users are eligible for replays, ensuring you limit recordings to specific users or cohorts.
In the Statsig Console, you can configure your privacy settings under **Project Settings → Analytics & Session Replay**. You must be a project admin to modify these settings.
### Baseline privacy options
* **Passwords (Default)**: Statsig replaces only password inputs with asterisks (\*). All other text and inputs appear as-is.
* **Inputs**: Statsig replaces all text in inputs with asterisks (\*). All other text appears as-is.
* **Maximum**: Statsig replaces all text and inputs with asterisks (\*).
### Selector rules
Use CSS selectors to precisely control how individual elements are handled during session replay: masked, unmasked, or blocked.
* **Masking** and **unmasking** apply only to text content. Masked text is replaced with asterisks (\*).
* **Blocking** removes the element entirely from the replay and replaces it with a black placeholder of the same size.
* **You can't unmask password inputs**, regardless of selector rules.
Selector rules override the baseline privacy settings. When multiple selector rules apply to the same element, Statsig enforces the following precedence: **Block → Mask → Unmask**
The examples below show how precedence is enforced.
```js
// Everything within the blocked class will
// appear as a single black placeholder
I will be part of the black placeholder
```
```js
// The closest rule will apply
Masked Text
```
```js
// With conflicting rules applied at the same level,
// the higher precedence will apply
```
```js
// With baseline privacy setting set to Maximum, all text is masked
// by default but this can be overwritten by unmasking
Masked Text
```
All selectors must be valid CSS selectors. For details on supported selector syntax, refer to MDN’s list of CSS selectors.
{% callout type="warning" %}
Using selector rules or baseline privacy settings besides Passwords will
overwrite the `maskTextFn`, `maskInputFn`, `maskTextSelector`, `maskAllInputs`,
`maskInputOptions`, and `blockSelector` options you passed in during
initialization.
{% /callout %}
### Global targeting gate
The Global Targeting Gate controls who is *eligible* for session recording. If a user doesn't pass this gate, Statsig never records their sessions. By default, this is set to Everyone, meaning there are no restrictions and Statsig can record anyone. This gate defines the upper bound of session recording eligibility.
{% callout type="warning" %}
If you are using bootstrapping, contact the Statsig team to confirm
your server SDK is supported.
{% /callout %}
================================================================================
# Debug Statsig Session Replay
URL: https://docs.statsig.com/session-replay/debug
Updated: 2026-07-11
================================================================================
## Large session recording warning
In the Console, you may encounter the warning:
> "This session recording is too large to load."
This occurs when a session exceeds 50 MB in size. Once Statsig records a session at this size, the console can't display it, and you can't fix it retroactively. If you consistently see this warning, here are a few steps you can take to reduce session sizes going forward:
1. **Disable Inline Stylesheets**\
When initializing the `SessionReplayClient`, set `inlineStylesheet` to `false`. Statsig generally recommends keeping `inlineStylesheet` set to `true`, because it ensures recordings accurately reflect your original CSS even if you later update your styles. However, this setting often causes sessions to grow very large and is the most common source of bloat.
{% tabs %}
{% tab title="Javascript" %}
```jsx
runStatsigSessionReplay(client, {
inlineStylesheet: false,
});
```
{% /tab %}
{% tab title="React" %}
```jsx
...
options={{
plugins: [
new StatsigSessionReplayPlugin({
inlineStylesheet: false,
}),
],
}}
...
```
{% /tab %}
{% /tabs %}
2. **Exclude Large Static Elements**\
If certain elements on your page are large but static (for example, background images or videos), you can exclude them from session capture by adding the `rr-block` class to their `className`. This prevents Statsig from recording those elements and can significantly reduce session size.
3. **Reach Out for Assistance**\
If you continue to experience large sessions, contact the team in [Slack Community](https://statsig.com/slack). A Statsig team member can review your session and provide recommendations for your setup.
================================================================================
# Watch Session Replays
URL: https://docs.statsig.com/session-replay/watch
Updated: 2026-07-11
================================================================================
To find Session Replays, go to the User’s group in the Statsig console’s navigation panel.
{% figure %}

{% /figure %}
The main interface has three sections. The leftmost column lists available replays and filter controls. The middle playback surface lets you play, pause, and skip to events of interest. Select the next button to jump to the next event, or select an event in the rightmost events panel to skip to that point in the replay timeline.
{% figure %}

{% /figure %}
For a more immersive replay experience you can hide the events panel:
{% figure %}

{% /figure %}
You can also enter full screen mode.
## Find a replay
{% figure %}

{% /figure %}
Select a replay from the list on the left to watch it. The replay card shows details such as URL, browser, country, and more, which you can use to scan for or filter to replays of interest.
To narrow the set of replays, add filters. Filters let you scope to replays that contain a specific event, user, Feature Gate exposure (pass or fail), or Experiment group exposure.
## Playlists
Playlists let you organize related session recordings into curated collections focused on specific user behaviors, issues, or research questions.
## Key features
* Organized Collections: Create named playlists to group related session recordings. This makes it easier to focus on specific user journeys, bug reports, or research topics without searching through large numbers of individual sessions.
* Collaborative Analysis: Share playlists with team members to collaborate on user experience analysis. Team members can view the same curated set of recordings to discuss findings and insights.
* Persistent Organization: Unlike temporary filters, playlists preserve your organizational structure, making it easy to return to specific sets of recordings for ongoing analysis or follow-up research.
* Integration: Playlists work within the existing session replay interface. When you open a playlist, you can view all recordings using the same replay player and analysis tools.
================================================================================
# CLI Session Replay
URL: https://docs.statsig.com/session-replay/cli-session-replay
Updated: 2026-07-11
================================================================================
CLI Session Replay allows you to record terminal sessions in your Node.js CLI applications and replay them in the Statsig Console. Use it to understand how users interact with your command-line tools, diagnose issues, and improve the user experience.
The plugin records terminal output and resize events.
User input is planned for a future version. Because user input is likely to contain sensitive information,
Statsig will release it after introducing methods to pause, filter, or redact recordings.
## Installation
Install the CLI session replay package for Node.js:
{% tabs %}
{% tab title="npm" %}
```bash
npm install @statsig/js-client @statsig/cli-session-replay-node
```
{% /tab %}
{% tab title="yarn" %}
```bash
yarn add @statsig/js-client @statsig/cli-session-replay-node
```
{% /tab %}
{% tab title="pnpm" %}
```bash
pnpm add @statsig/js-client @statsig/cli-session-replay-node
```
{% /tab %}
{% /tabs %}
## Basic usage
```javascript
import { StatsigClient } from '@statsig/js-client';
import { StatsigCliSessionReplayPlugin } from '@statsig/cli-session-replay-node';
const client = new StatsigClient(
'your-client-key',
{ userID: 'user-123' },
{
loggingEnabled: 'always', // Required for CLI environments
plugins: [new StatsigCliSessionReplayPlugin()],
}
);
// Recording starts here
console.log('Hello from CLI!');
await client.initializeAsync();
// Your CLI application logic here
console.log('Continue');
```
{% callout type="note" %}
**CLI logging requirement**: CLI applications must set `loggingEnabled: 'always'` when initializing the StatsigClient. By default, Statsig enables logging only in browser environments. CLI session replay requires logging to be enabled in all environments to capture and send session data.
{% /callout %}
## Configuration options
The `StatsigCliSessionReplayPlugin` accepts optional configuration:
```javascript
import { StatsigCliSessionReplayPlugin } from '@statsig/cli-session-replay-node';
const plugin = new StatsigCliSessionReplayPlugin({
// Override the start timestamp (in milliseconds)
startTimestamp: Date.now(),
// Custom Asciicast header properties
asciicastHeader: {
title: 'My CLI App Session',
command: 'my-cli-tool --verbose',
env: {
TERM: 'xterm-256color',
SHELL: '/bin/bash'
},
}
});
```
### Configuration properties
* **`startTimestamp`** (optional): Override the recording start time in milliseconds. Defaults to `Date.now()`.
* **`asciicastHeader`** (optional): Custom properties for the Asciicast header. For details, refer to the [Asciicast v2 File Format page](https://docs.asciinema.org/manual/asciicast/v2/#header). Common fields include:
* `title`: Human-readable title for the recording
* `command`: The command that was executed
* `env`: Environment variables relevant to the session
* `theme`: Terminal color theme object.
## Recording limits
* **Duration**: Sessions automatically end after 4 hours.
* **Size**: Recording stops if session data exceeds 1 MB.
## Viewing recordings
CLI session recordings appear in the Statsig Console alongside web session replays. Each recording shows exactly what happened in the terminal, including:
* All terminal output
* Terminal resize events
* Timing information for each interaction
* Session metadata and environment details
## Manual recording control
You can access the recording instance for manual control:
```javascript
import { CliRecording } from '@statsig/cli-session-replay-node';
// Check if currently recording
if (CliRecording.isRecording()) {
console.log('Session is being recorded');
}
// Get current recording instance
const recording = CliRecording.currentRecording;
// Manually finish recording
CliRecording.finish();
```
## Platform support
CLI Session Replay supports:
* Node.js applications
* Linux, macOS, and Windows terminals
* Any terminal that supports standard input/output streams
================================================================================
# Overview
URL: https://docs.statsig.com/integrations/mcp/overview
Updated: 2026-07-11
================================================================================
## MCP configuration guides
{% card-grid columns="2" %}
{% card title="Codex" href="/integrations/mcp/codex" icon="computer" %}
Set up Statsig MCP using Codex Desktop App, CLI, or IDE extension.
{% /card %}
{% card title="ChatGPT Connector" href="/integrations/mcp/chatgpt-connector" icon="message" %}
Talk to your Statsig projects from within ChatGPT.
{% /card %}
{% /card-grid %}
{% card-grid columns="3" %}
{% card title="Cursor" href="/integrations/mcp/cursor" %}
Configure Statsig MCP in Cursor IDE.
{% /card %}
{% card title="Claude Code" href="/integrations/mcp/claude-code" %}
Set up Statsig MCP in Claude Code.
{% /card %}
{% card title="Manual setup" href="/integrations/mcp/manual-setup" %}
Manual configuration for any MCP-compatible tool.
{% /card %}
{% /card-grid %}
## Current MCP capabilities
{% accordion-group %}
{% accordion title="Audit Logs" %}
{% table %}
- Tool
- Description
---
- Get_Audit_Logs
- List audit logs in the project. Filter by:
- `id`
- `sortKey`
- `sortOrder`
- `tags`
- `startDate`
- `endDate`
{% /table %}
{% /accordion %}
{% accordion title="Dynamic Configs" %}
{% table %}
- Tool
- Description
---
- Create_Dynamic_Config
- Create new config
---
- Get_Dynamic_Config_Details_by_ID
- Retrieve detailed config information
---
- Get_List_of_Dynamic_Configs
- List all dynamic config objects in the project. Filter by:
- `creatorName`
- `tags`
---
- Update_Dynamic_Config_Entirely
- Replace entire dynamic config with new targeting and values
{% /table %}
{% /accordion %}
{% accordion title="Experiments (A/B Tests)" %}
{% table %}
- Tool
- Description
---
- Create_Experiment
- Create new experiment
---
- Get_Experiment_Details_by_ID
- Get experiment details. Optionally trim the response to only the fields you need:
- `fields`: comma-separated list of field names to return (for example, `hypothesis,primaryMetrics`); omit to return all fields. Reduces context consumption by up to 95–99%.
---
- Get_Experiments_Batch
- Read multiple experiments in a single call. Accepts a list of experiment IDs and an optional `fields` parameter to return only specific fields across all requested experiments.
---
- Get_Experiment_Overall_Results
- Retrieve experiment results/pulse data for a specific experiment.
Analyze by:
- `date`
- `cuped`
- `confidence`
---
- Get_Experiment_Metric_Dimension_Results
- Retrieve metric results for one experiment metric with dimensional
breakdowns. Analyze by:
- `date`
- `cuped`
- `confidence`
---
- Get_List_of_Experiments
- List all experiments in the project. Filter by:
- `status`
- `creatorName`
- `tags`
- `stale`
---
- Update_Experiment_Entirely
- Replace entire experiment configuration (Statsig removes any excluded data)
{% /table %}
{% /accordion %}
{% accordion title="Autotunes" %}
**Create_Autotune**: Create a new Autotune (multi-armed bandit) experiment. Describe the arms, success event, exploration/attribution windows, and winner threshold; the agent builds and creates the Autotune. Creates in draft by default; start the Autotune from the console to allocate traffic. Prompts for confirmation before creating.
{% /accordion %}
{% accordion title="Gates (Feature Flags)" %}
{% table %}
- Tool
- Description
---
- Create_Gate
- Create new gate/flag
---
- Get_Gate_Details_by_ID
- Get complete gate configuration details
---
- Get_Gate_Results
- Retrieve pulse/results for a specific gate rule.
- `cuped`
- `confidence`
---
- Get_List_of_Gates
- List all gates/flags. Filter by:
- `type`
- `creatorName`
- `tags`
---
- Update_Gate_Entirely
- Replace entire gate setup with new rules and settings (Statsig removes any excluded data)
{% /table %}
{% /accordion %}
{% accordion title="Layers" %}
{% table %}
- Tool
- Description
---
- Create_Layer
- Create a new layer
---
- Get_Layer_Details_by_ID
- Retrieve layer details, including parameters and metadata
---
- Get_List_of_Layers
- List all layers in the project
---
- Update_Layer_Entirely
- Replace the full layer configuration
{% /table %}
{% /accordion %}
{% accordion title="Metrics and Metric Sources" %}
{% table %}
- Tool
- Description
---
- Get_List_of_Metric_Sources
- List all metric sources in the project
---
- Get_List_of_Metrics
- List all metrics in the project. Filter by:
- `showHiddenMetrics`
- `tags`
- `filters`
---
- Get_Metric_Definition_by_ID
- Get the full definition for a metric, including its type, source, and configuration details
{% /table %}
{% /accordion %}
{% accordion title="Parameter Stores" %}
{% table %}
- Tool
- Description
---
- Create_Param_Store
- Create a new Parameter Store and its parameters
---
- Update_Param_Store
- Update an existing Parameter Store, including adding or changing parameters
---
- Delete_Param_Store
- Delete a Parameter Store
{% /table %}
{% /accordion %}
{% accordion title="Segments" %}
{% table %}
- Tool
- Description
---
- Create_Segment
- Create a new segment. Supports `id_list`, `rule_based`, `analysis_list`, and `user_store_id_list` segment types
---
- Get_List_of_Segments
- List all segments in the project
---
- Get_Segment_Details_by_ID
- Retrieve segment details
---
- Update_Segment
- Update an existing segment. Specifically:
- Update rules for conditional segment types
- Add IDs to user stores and ID lists
{% /table %}
{% /accordion %}
{% /accordion-group %}
Need other functions? Statsig considers additions by request. Reach out in Slack.
## Use cases
The Statsig MCP server supports both `GET` and `POST` requests. Read-only users can connect and use all read tools. Write tools require an API key with write permissions. The Statsig MCP server is especially useful for:
- Repetitive tasks like cleaning up stale gates
- Summarizing console information in your IDE workflows
- Bulk creating or deleting gates, and making the necessary changes in your code
### Example prompt for stale gate cleanup
```
You are an expert, diligent Software engineer with the sole goal of reducing the amount of tech debt in the code base. This code base, making use of best practices, leverages feature gates liberally using Statsig. As gates complete their lifecycle in Statsig, they may end up "stale" which means that they're enabled, but no longer checked. Your job is to find these gates, and refactor the codebase to no longer check the gate (instead, changing the check to a constant value).
You should follow coding best practices:
- You should not simply replace gate calls with "True" or "False" but instead carefully trace the logic through to where it is used and change the behavior that way - adjusting the code in minor ways to make the default behavior what the value is that the gate was returning
- You should always strive to write minimal code - readable but terse, never longer than it needs to be
- You should never write comments or debug statements.
You should use the statsig-local MCP to list feature gates, then look for gates that are marked as stale. You should then grep the codebase for that feature flag name, and do a minimal rewrite of the code to no longer use Statsig, removing the checkGate call or similar. When you use the MCP use the get /console/v1/gates endpoint and parameters type="STALE" and limit =10. You should select only one gate to do this with, before stopping. If you cannot find the gate after a grep, try the next one you found using the MCP. Once you successfully remove a gate, return.
```
================================================================================
# Docs MCP server
URL: https://docs.statsig.com/integrations/mcp/docs-mcp-server
Updated: 2026-07-11
================================================================================
Use the Statsig Docs MCP server to let supported AI clients read public Statsig documentation directly from `https://docs.statsig.com/api/mcp`. The server is read-only and serves English documentation from this site, so your AI tools answer Statsig questions with current, accurate docs content.
This server is separate from the authenticated [Statsig MCP server](/integrations/mcp/overview), which connects to your project data. The Docs MCP server reads documentation only and needs no API key.
## Supported clients
Use a client that supports remote MCP servers over Streamable HTTP and accepts a direct URL. Claude Code, Cursor, and Codex CLI all work.
Older MCP clients that only support the deprecated HTTP+SSE transport can't connect to this endpoint.
## Add the server
Many MCP clients read a JSON config:
```json
{
"mcpServers": {
"statsig-docs": {
"url": "https://docs.statsig.com/api/mcp",
"transport": "streamable-http"
}
}
}
```
If your client supports command-line setup, add the same URL as a remote HTTP MCP server:
```bash
claude mcp add --transport http statsig-docs https://docs.statsig.com/api/mcp
```
## Available tools
The server exposes three read-only tools:
- `get_page`: Retrieve the full markdown content of a Statsig documentation page.
- `list_pages`: List all available documentation pages with their titles and slugs.
- `search_docs`: Search documentation pages by keyword.
## Limitations
{% callout type="note" heading="" %}
The Docs MCP server is public, read-only, and serves English documentation only. It doesn't require authentication, doesn't expose MCP prompts or resources, and supports Streamable HTTP only. The server rate-limits requests per client.
{% /callout %}
For authenticated access to your project data, like experiments, gates, and metrics, use the [Statsig MCP server](/integrations/mcp/overview) instead.
## Troubleshooting
- If your client reports an unsupported transport, update it to a version that supports remote MCP servers over Streamable HTTP.
- If setup fails, confirm that you used the exact endpoint URL: `https://docs.statsig.com/api/mcp`.
- If you need project data or write access, this server isn't the right endpoint. Use the authenticated [Statsig MCP server](/integrations/mcp/overview).
================================================================================
# Statsig MCP with Codex
URL: https://docs.statsig.com/integrations/mcp/codex
Updated: 2026-07-11
================================================================================
## Installation and authentication with OAuth
{% callout type="note" %}
MCP OAuth only supports Personal Console API Keys. Ensure your Statsig org
owner has enabled Personal Console API Keys creation for your role
[here](https://console.statsig.com/settings?tab=organization){" "}
{% /callout %}
On Codex, use OAuth and the HTTP transport directly. Authentication is saved for future sessions.
{% tabs %}
{% tab title="Codex Desktop" %}
In Codex Desktop, navigate to *Settings > MCP servers* and add a new custom server. Set up using Streamable HTTP, with URL set to `https://api.statsig.com/v1/mcp`:

Upon saving, you should see statsig turned on under custom servers.

{% /tab %}
{% tab title="Codex CLI and IDE extension" %}
If you are working in Codex CLI or IDE extension, run the below command to add the Statsig MCP server into your `~/.codex/config.toml` file and restart Codex.
```bash
codex mcp add statsig --url https://api.statsig.com/v1/mcp
```
This command opens a browser window for you to sign in to your Statsig account and authorize access to your Statsig project.
After signing in, restart Codex and run `/mcp` in CLI to confirm the Statsig MCP is listed as an available MCP server. Make sure the status is set to **enabled**:

{% /tab %}
{% /tabs %}
{% accordion title="Using API key instead of OAuth" %}
```toml
[mcp_servers.statsig]
url = "https://api.statsig.com/v1/mcp"
command = "npx"
args = ["--yes", "mcp-remote", "https://api.statsig.com/v1/mcp", "--header", "statsig-api-key: console-YOUR-CONSOLE-API-KEY"]
trust_level = "trusted"
enabled = true
```
Replace `console-YOUR-CONSOLE-API-KEY` with your actual Statsig Console API key, which you can retrieve [here](https://console.statsig.com/api_keys). Ensure your API key has the right permissions: read-only keys can view data, while write keys can make changes to your project.
{% /accordion %}
## Using Statsig MCP with Codex
With Statsig MCP configured in any Codex environment, you can:
* **Explore Experiments**: "List all my active experiments"
* **Manage Gates**: "What gates are currently stale?"
* **Configure Dynamic Configs**: "Show me the configuration for the dynamic config 'dynamic-config'"
* **Get Insights**: "Show me details about the experiment called 'new-checkout-flow'"
## Next steps
After installation, you can:
* List experiments, gates, and dynamic configs
* Create and update experiments, gates, and configs
* Access your Statsig data directly from Codex
For available MCP capabilities, go to the [MCP capabilities](/integrations/mcp/overview#current-mcp-capabilities) section.
================================================================================
# Statsig ChatGPT App
URL: https://docs.statsig.com/integrations/mcp/chatgpt-connector
Updated: 2026-07-11
================================================================================
## How the Statsig ChatGPT app works
The ChatGPT App is built on top of the Statsig MCP server. After setup, you can query experiments, manage feature flags, and explore analytics directly within OpenAI's ChatGPT conversational interface.
## Installation
1. Navigate to the Statsig App in the ChatGPT Apps Directory [here](https://chatgpt.com/apps/statsig/asdk_app_6967f065ac9481918969c660ff7686e9).
2. Click "Connect" and "Continue" to complete OAuth for your Statsig project.
{% callout type="note" %}
ChatGPT App OAuth only supports Personal Console API Keys. Ensure your
Statsig org owner has enabled Personal Console API Keys creation for your
role [here](https://console.statsig.com/settings?tab=organization)
{% /callout %}
{% figure %}
{% /figure %}
## Using Statsig MCP within ChatGPT
With Statsig MCP configured in ChatGPT, you can:
- **Explore Experiments**: "List all my active experiments"
- **Manage Gates**: "What gates are currently stale?"
- **Configure Dynamic Configs**: "Show me the configuration for the dynamic config 'dynamic-config'"
- **Get Insights**: "Show me details about the experiment called 'new-checkout-flow'"
## What you can do after installation
- List experiments, gates, and dynamic configs
- Create and update experiments, gates, and configs
- Access your Statsig data directly from ChatGPT
For available MCP capabilities, go to the [MCP capabilities](/integrations/mcp/overview#current-mcp-capabilities) section.
================================================================================
# Statsig MCP with Cursor
URL: https://docs.statsig.com/integrations/mcp/cursor
Updated: 2026-07-11
================================================================================
## Installation and authentication with OAuth
{% callout type="note" %}
MCP OAuth only supports Personal Console API Keys. Ensure your Statsig org
owner has enabled Personal Console API Keys creation for your role
[here](https://console.statsig.com/settings?tab=organization){" "}
{% /callout %}
You can add the Statsig MCP with OAuth to Cursor in two ways:
### Option 1: Quick install (recommended)
[Click here](cursor://anysphere.cursor-deeplink/mcp/install?name=statsig\&config=eyJ1cmwiOiJodHRwczovL2FwaS5zdGF0c2lnLmNvbS92MS9tY3AifQ%3D%3D) to automatically add the Statsig MCP to Cursor.
### Option 2: Manual configuration
1. Open Cursor settings
2. Navigate to **Settings → Cursor Settings → Tools & Integrations**
3. Find the MCP servers section
4. Add the following configuration to `~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"statsig": {
"url": "https://api.statsig.com/v1/mcp"
}
}
}
```
Cursor automatically handles OAuth authentication when you first use the Statsig MCP. You'll be prompted to:
1. Sign in to your Statsig account
2. Authorize the MCP server to access your Statsig project
3. Restart Cursor to apply the changes
4. Verify the connection by navigating to *Settings → Cursor Settings → Tools & Integrations*, where the Statsig MCP server should appear and be active
Cursor saves the authentication for future sessions.
{% accordion title="Using API key instead of OAuth" %}
OAuth is the recommended setup, but you can also authenticate with your Console API key. [Click here](cursor://anysphere.cursor-deeplink/mcp/install?name=statsig\&config=eyJjb21tYW5kIjoibnB4IG1jcC1yZW1vdGUgaHR0cHM6Ly9hcGkuc3RhdHNpZy5jb20vdjEvbWNwIC0taGVhZGVyIHN0YXRzaWctYXBpLWtleToke0FVVEhfVE9LRU59IiwiZW52Ijp7IkFVVEhfVE9LRU4iOiJpbnNlcnQteW91ci1hcGkta2V5LWhlcmUifX0%3D) to quick install, or manually configure:
1. Open Cursor settings
2. Navigate to **Settings → Cursor Settings → Tools & Integrations**
3. Find the MCP servers sections
4. Add the below configuration to `~/.cursor/mcp.json`
```json
{
"mcpServers": {
"statsig": {
"command": "npx mcp-remote https://api.statsig.com/v1/mcp --header statsig-api-key:${AUTH_TOKEN}",
"env": {
"AUTH_TOKEN": "console-YOUR-CONSOLE-API-KEY"
}
}
}
}
```
Replace `console-YOUR-CONSOLE-API-KEY` with your actual Statsig Console API key, which you can retrieve [here](https://console.statsig.com/api_keys). Ensure your API key has the right permissions: read-only keys can view data, while write keys can make changes to your project.
{% /accordion %}
## Using Statsig MCP with Cursor
After configuration, you can use Statsig MCP commands in Cursor's chat interface:
* **Query Experiments**: "What experiments are currently running?"
* **Manage Gates**: "List all my feature flags"
* **Get Details**: "Show me the configuration for gate 'new-feature'"
* **Create Entities**: "Create a new experiment called 'checkout-test'"
## Troubleshooting
If the MCP server doesn't appear:
* Make sure you've restarted Cursor after adding the configuration
* Check that the `mcp.json` file is in the correct location: `~/.cursor/mcp.json`
* Verify your Statsig account has the necessary permissions
## Next steps
* [MCP capabilities](/integrations/mcp/overview#current-mcp-capabilities)
* [Use cases](/integrations/mcp/overview#use-cases) for Statsig MCP
* [Stale gate cleanup example](/integrations/mcp/overview#example-prompt-for-stale-gate-cleanup)
================================================================================
# Statsig MCP with Claude Code
URL: https://docs.statsig.com/integrations/mcp/claude-code
Updated: 2026-07-11
================================================================================
## Installation and authentication with OAuth
{% callout type="note" %}
MCP OAuth only supports Personal Console API Keys. Ensure your Statsig org
owner has enabled Personal Console API Keys creation for your role
[here](https://console.statsig.com/settings?tab=organization){" "}
{% /callout %}
On Claude Code, use OAuth and the HTTP transport directly. Run this command:
```bash
claude mcp add --transport http statsig https://api.statsig.com/v1/mcp
```
This command:
* Adds the Statsig MCP server to your Claude Code configuration
* Configures it to use HTTP transport with OAuth authentication
* Sets up the connection to Statsig's MCP endpoint
To authenticate with OAuth, run `/mcp` in Claude Code and follow the setup instructions:
1. Claude Code opens a browser window
2. Sign in to your Statsig account
3. Authorize the MCP server to access your Statsig project
4. Claude Code saves the authentication for future sessions
{% accordion title="Using API key instead of OAuth" %}
OAuth is the recommended setup, but you can also authenticate with your Console API key. Run this command on the command line:
```bash
claude mcp add --transport http statsig-local https://api.statsig.com/v1/mcp \
--header "statsig-api-key: console-YOUR-CONSOLE-API-KEY"
```
Replace `console-YOUR-CONSOLE-API-KEY` with your actual Statsig Console API key, which you can retrieve [here](https://console.statsig.com/api_keys). Ensure your API key has the required permissions: read-only keys can view data, while write keys can make changes to your project.
{% /accordion %}
## Verify the connection
After installation, verify the connection:
1. Open Claude Code.
2. Ask Claude to list your Statsig experiments or gates.
3. Confirm that Claude can access your Statsig data.
## Using Statsig MCP with Claude Code
After configuration, you can interact with your Statsig data through Claude Code:
* **Query Experiments**: "What experiments are currently running?"
* **Manage Gates**: "List all my feature flags"
* **Get Details**: "Show me the configuration for gate 'new-feature'"
* **Create Entities**: "Create a new experiment called 'checkout-test'"
## Troubleshooting
If you encounter issues:
* Make sure you have the latest version of Claude Code
* Verify your Statsig account has the necessary API permissions
* Check that the MCP server URL is correct: `https://api.statsig.com/v1/mcp`
* Try re-running the installation command
## Related resources
* [MCP capabilities](/integrations/mcp/overview#current-mcp-capabilities)
* [Use cases](/integrations/mcp/overview#use-cases) for Statsig MCP
* Set up Statsig MCP in other tools: [Cursor](/integrations/mcp/cursor), [Codex CLI](/integrations/mcp/codex)
================================================================================
# Statsig MCP with Other MCP-Compatible Clients
URL: https://docs.statsig.com/integrations/mcp/manual-setup
Updated: 2026-07-11
================================================================================
If your tool doesn't have a specific setup guide, you can manually configure the Statsig MCP server. This guide covers the general configuration steps that work with any MCP-compatible client.
## Prerequisites
* A Statsig account (sign up at [console.statsig.com](https://console.statsig.com))
* An MCP-compatible tool or client
* Access to your tool's configuration files
## Configuration
Add the following configuration to your MCP client's configuration file:
```json
{
"mcpServers": {
"statsig": {
"url": "https://api.statsig.com/v1/mcp"
}
}
}
```
This installs the Statsig MCP Server with OAuth. When you first use the MCP server:
1. Your tool prompts you to authenticate
2. You'll be redirected to Statsig's OAuth page
3. Sign in and authorize the MCP server
4. The authentication token is stored automatically
{% callout type="note" %}
MCP OAuth only supports Personal Console API Keys. Ensure your Statsig org
owner has enabled Personal Console API Keys creation for your role
[here](https://console.statsig.com/settings?tab=organization){" "}
{% /callout %}
{% accordion title="Using API Key instead of OAuth" %}
OAuth is the recommended setup, but you can also authenticate with your Console API key. Add the below configuration to your MCP client's configuration file:
```json
{
"mcpServers": {
"statsig": {
"command": "npx mcp-remote https://api.statsig.com/v1/mcp --header statsig-api-key:${AUTH_TOKEN}",
"env": {
"AUTH_TOKEN": "console-YOUR-CONSOLE-API-KEY"
}
}
}
}
```
Replace `console-YOUR-CONSOLE-API-KEY` with your actual Statsig Console API key, which you can retrieve [here](https://console.statsig.com/api_keys). Ensure your API key has the right permissions: read-only keys can view data, while write keys can make changes to your project.
{% /accordion %}
## Verification
After adding the configuration:
1. Restart your tool to apply the changes
2. Check your tool's MCP server list to verify Statsig appears
3. Try using a Statsig MCP command to test the connection
## Testing the connection
You can test the connection by asking your tool to:
* **List experiments**: "Get list of experiments"
* **List gates**: "Get list of gates"
* **Get details**: "Get details for experiment \[experiment-id]"
## Next steps
* [MCP capabilities](/integrations/mcp/overview#current-mcp-capabilities)
* [Use cases](/integrations/mcp/overview#use-cases) for Statsig MCP
* Tool-specific guides: [Cursor](/integrations/mcp/cursor), [Claude Code](/integrations/mcp/claude-code), [Codex CLI](/integrations/mcp/codex)
================================================================================
# Agent Skills Repository
URL: https://docs.statsig.com/integrations/agent-skills
Updated: 2026-07-11
================================================================================
The [statsig-io/agent-skills](https://github.com/statsig-io/agent-skills) repository contains reusable skill packs for coding agents. Skills are packaged, shareable workflows for repeated tasks, built on top of the Statsig MCP tools and Console API.
These skills build on the open [Agent Skills standard](https://agentskills.io/).
## Available skills
| Name | Description |
| --- | --- |
| `statsig` | Manage Statsig experiments, gates, dynamic configs, segments, layers, and audit logs through the Statsig MCP. |
| `statsig-create-cloud-metric` | Draft or execute Statsig Cloud metric creation requests through the Statsig Console API. |
| `statsig-dashboard` | Create, read, and update your Statsig dashboards. |
## Install from the repository
Install a skill from this repo using the Vercel `skills` CLI:
```bash
npx skills add statsig-io/agent-skills
```
You can also:
* List installable skills: `npx skills add statsig-io/agent-skills --list`
* Install a skill globally for your user: `npx skills add -g statsig-io/agent-skills --skill statsig-dashboard`
* Install every skill in this repo: `npx skills add statsig-io/agent-skills --all`
After installation, compatible agents can discover each skill from its metadata.
## Requirements
* `STATSIG_CONSOLE_API_KEY` for Statsig Console API access
* Python 3 if you want to run bundled helper scripts directly
================================================================================
# AI Development with Statsig
URL: https://docs.statsig.com/integrations/ai_development_with_statsig
Updated: 2026-07-11
================================================================================
## How Statsig supports AI development
Developers increasingly use AI agents for small-to-midsized tasks. For small feature additions, this is low risk but can still introduce bugs or low-quality user experiences. When an agent is aware of Statsig, the agent can automatically add logging for potential errors and add feature gates to new features (or confirm they are behind existing gates), so you can manage rollouts safely and measure any degradation.
This becomes significant given the high velocity individuals can now achieve with AI assistance, including the ability to develop within codebases they have less context in.
## Recommended approach
Use an `AGENTS.md` file (or an equivalent file such as `CLAUDE.md`) to provide instructions you can tailor to your workflow.
The critical behaviors are:
* Adding gates to new features or codepaths, and tuning how aggressively to do so
* Automatically adding your user ID, and any test IDs your agent may log in as, to those gates for testing in development
* Adding critical logs for errors and user behaviors
* Adding these to tracking is manual now, but Statsig has plans to make this automatable through MCP for event count metrics.
### Boilerplate `Agents.md` file
Fill in your Statsig ID, and follow the [MCP guide](/integrations/mcp/overview) to set up the MCP server. Consider updating naming conventions and logic around when to ask.
Use a limited-access console API key to reduce the risk of an agent deleting entities in Statsig.
```
# Statsig Development Guidelines
It is important that we selectively killswitch new features or any new, risky codepaths using Statsig Feature Gates so that we can turn them off if they cause issues.
## When To Use Gates
- We should consider adding a gate whenever we make significant changes to product features that have any risk of causing issues.
- It is possible for this to be spammy, so clarify with users during planning what should be killswitched.
- Start by assuming atomic, session-level features will be killswitched - e.g. "A new modal" would be, but not a copy change within a modal, or a bugfix.
- Sometimes, adding a gate is just as dangerous as the feature itself.
- Identify situations where this could occur - usually when adding the gate adds a large amount of complexity - and flag the risk to the user when asking if they want to create a gate.
Always ask the user if they want to gate the feature. Often, users will want to include the feature behind an existing gate, so ask in this way, when appropriate:
"This seems like a feature that you might want to be able to turn off remotely. Would you like to gate this feature, add it to an existing gate, or proceed without using gates?"
Look for gates already being used for this feature type - often in the same file or nearby in the file tree for client code.
Don't spend a bunch of time investigating code before asking, since it leads to a lot of hang time. This should be an early step in the process.
Quickly check in with the user on gating before you do a deep codebase scan - though you might want to use a quick context check to identify risk.
## How to Use Gates
- Create a new gate, if requested, using the statsig-local MCP server. If this server does not exist, ignore the above.
- After creation, use that gate in code, following the local codebase patterns for accessing statsig.
- This will normally be Statsig.useGate() or similar, but they may have an internal wrapper or use a general feature flag library which wraps Statsig.
- When creating the gate, start it with a default-fail rules. The user, or we, can add rules later to release the gate.
- Add a rule called "Development" which passes the user's ID, specified below. Make sure this is the first evaluated rule.
## Override instructions
- The user's ID is the string ""
- Use a custom ID gate condition, with the userID field set to that ID
## Naming Conventions
- For these features, create a feature gate which is a 3-4 word description separated by dashes.
- Do not use the word killswitch. Propose the name of the gate before creating it, allowing the user to update it if desired. Do not include your name (e.g. codex, claude, cursor) in the gate name.
## Logging
- We would like to measure our gate changes as we roll out features.
- If there's key interactions in the code which are not logged to Statsig, consider logging those events to statsig
- Include critical metadata (e.g. information about what happened, key numeric values)
Propose these logging changes to the user and see what they say, and only if they're very clearly needed. Similar to the gate checks, pattern match to what is in the repo already, e.g.
Statsig.logEvent or a wrapper like LoggingContext.log()
## Statsig Implementation Instructions
[left empty - fill with specific instructions about your Statsig implementation if it exists, such as specific calling patterns or gotchas]
## Guardrails and Critical Rules
- In this context, only use this MCP server to create gates
- Never run deletions or other destructive tools without explicit instructions from the user
```
================================================================================
# Github AI Integration
URL: https://docs.statsig.com/integrations/github-ai-integration
Updated: 2026-07-11
================================================================================
## How the GitHub AI integration works
{% callout type="info" %}
This feature might have rough edges. If you have feedback, share it with the Statsig team.
{% /callout %}
The Statsig GitHub integration links your organization's code repository directly to your Statsig project. Statsig analyzes your codebase and creates a private Knowledge Graph that maps relationships between your codebase and Statsig services such as Feature Gates, Experiments, and Metrics.
This integration connects your software development with measurable business impact, delivering contextual insights within Statsig.
{% figure %}

{% /figure %}
### Key features
Integrating your code repository unlocks the following capabilities:
* Locate specific lines of code where a Feature Gate or Experiment is implemented
* Automatically generate PRs (Pull Requests) to remove stale flags from your code
{% figure %}

{% /figure %}
* Understand the intent behind metrics and experiments based on code implementation
{% figure %}

{% /figure %}
* Write natural language queries to create charts to answer business questions (Coming Soon)
## Why connect your codebase
In modern product development, there is often a disconnect between the code you write (hosted on GitHub) and its impact and business outcomes (data in Statsig). The Knowledge Graph bridges this gap by analyzing your connected repositories to provide the context behind every feature.
* Context: The Knowledge Graph knows when an event is logged or a feature is exposed to a user.
* Intent: The Knowledge Graph understands the purpose and meaning of that event and feature.
* Freshness: The Knowledge Graph ensures insights in Statsig reflect the current state of your main branch.
## Installation guide
Before you begin, confirm you have Admin permissions in both your Statsig Project and the target GitHub Organization.
### Prerequisites
If your organization uses IP restrictions to limit access to your GitHub resources, you must allowlist the IP addresses that Statsig services use to connect to GitHub. Follow the steps in this [GitHub doc](https://docs.github.com/en/enterprise-cloud@latest/organizations/keeping-your-organization-secure/managing-security-settings-for-your-organization/managing-allowed-ip-addresses-for-your-organization) to allowlist IP addresses for your organization.
{% figure %}

{% /figure %}
If you choose to manually add IP addresses, go to [Statsig IP ranges](/infrastructure/statsig_ip_ranges#statsig-ip-ranges).
### First-time installation
If you're connecting GitHub to Statsig for the first time:
1. Navigate to **Settings** → **Integrations** in the Statsig console.
2. Locate and select the **Statsig GitHub App** card.
3. Select **Enable Connection**.
4. You are redirected to GitHub. Authenticate and select the target Organization and Repositories.
5. After authorization, you are redirected back to Statsig to confirm the connection.
{% callout type="info" %}
You can install the app on specific repositories or your entire organization. For the best results with Knowledge Graph, select all relevant repositories containing feature code.
{% /callout %}
### Connecting additional projects
GitHub allows a specific GitHub App to be installed only once per GitHub Organization. If you have multiple Statsig Projects (for example, "Project 1" and "Project 2") that need access to the same GitHub Org, they share the underlying connection.
To link a new project to an existing GitHub connection:
1. Navigate to **Settings** → **Integrations** in the Statsig console.
2. Locate and select the **Statsig GitHub App** card. Statsig indicates if an existing connection exists for any other project.
3. Select the Statsig Project you want to inherit the connection from.
4. Confirm the link.
### Migrating from legacy (PAT) integration
If you're using the older Personal Access Token (PAT) integration, upgrade to the GitHub App for improved security and functionality.
1. Navigate to **Settings** → **Integrations** in the Statsig console.
2. Locate and select the **Statsig GitHub App** card.
3. A banner on the GitHub integration card prompts you to upgrade from the PAT-based connection.
4. Follow the flow to install the GitHub App on your repositories.
5. After the app is installed, the system automatically deprecates the old PAT connection.
## Statsig GitHub integration FAQs
1. **Will my data be used to train LLM models?**\
Statsig doesn't use your data to train LLM models, and neither do its sub-processors. Your data is secured to your organization and used to improve the services provided to you.
2. **Does Statsig store my source code?**\
Statsig doesn't permanently store your source code. Statsig temporarily processes code to extract metadata and embeddings to create the Knowledge Graph, then discards it when no longer necessary.
3. **How long does it take for the code insights to appear in Statsig?**\
Setting up the integration can take up to a few hours depending on the size of the repository. After the initial setup, Statsig indexes new data every 7 days.
4. **What security controls apply?**\
Go to [Statsig security practices](https://www.statsig.com/trust/security) for security information. Go to [AI governance](/compliance/ai_governance_security_privacy) for AI governance information.
================================================================================
# Control Panel
URL: https://docs.statsig.com/control-panel/overview
Updated: 2026-07-11
================================================================================
Control Panel is a surface to measure and track features that you and your teams deploy and measure with Statsig. It is similar to the standard gate and experiment tables in Statsig. Control Panel lets you switch between filters on configs, experiments, and gates; measure results; make or review ship decisions; and manage development flows such as overrides.
## Using Control Panel
* Configure sections in the left column to specify different filters and display settings, such as "My Changes" or "My Team's Changes".
* View results inline. Configs and experiments show a summary of metric movements. Hover to see a table of all observed metric movements. Gates and Dynamic Configs can be opened to see results per rule.
* Manage configs. View or set your override status from Control Panel, and end features if unexpected behavior occurs.
================================================================================
# Metrics User Guide
URL: https://docs.statsig.com/metrics/introduction
Updated: 2026-07-11
================================================================================
## Metrics user guide
Statsig combines data from your existing data sources to give you a complete view of your product metrics and the impact new features and experiments have on those metrics.
Statsig automatically creates metrics from the **raw events** that you log from your application, as well as raw and transformed events you send to Statsig through a data collector.
Statsig can also reuse your existing **precomputed metrics** by natively integrating and pulling data from your cloud data warehouse. You can also ingest metrics into Statsig using an HTTP endpoint or Azure Blob storage.
{% figure %}

{% /figure %}
The **Metrics User Guide** consists of [Metrics 101](/metrics/101), [201](/metrics/201), and [301](/metrics/301) level guides that step through basic concepts to real-time analytics in Statsig.
## Metrics 101 - An overview
* [Introduction](/metrics/101)
* [Raw Events](/metrics/raw-events)
* [Raw Event Metrics](/metrics/raw-event-metrics)
* [Custom Metrics](/metrics/custom-metrics)
* [Precomputed Metrics](/metrics/precomputed-metrics)
* [Pulse Metrics](/metrics/pulse)
* [Metric Dimensions](/metrics/metric-dimensions)
## Metrics 201 - Diving deeper
* [Introduction](/metrics/201)
* [Creating Metrics](/metrics/create)
* [Tagging Metrics](/metrics/create-metric-tags)
* [User Metrics](/metrics/user)
* [Assignment ID != Analysis ID](/metrics/different-id)
## Metrics 301 - Advanced topics
* [Introduction](/metrics/301)
* [Metrics Explorer](/product-analytics/overview)
* [User Funnels](/metrics/create-user-funnels)
* [User Flows](/metrics/create-user-flows)
Statsig combines product experimentation with real-time analytics to support **product observability**.
{% figure %}

{% /figure %}
To get started, go to [Metrics 101](/metrics/101).
================================================================================
# Metrics Overview
URL: https://docs.statsig.com/metrics/101
Updated: 2026-07-11
================================================================================
{% callout type="info" %}
**Warehouse Native users**: You're viewing the Cloud docs for this page. Metrics and experiments behave differently in Warehouse Native. Read more in [Data & Semantic Layer in Warehouse Native](/statsig-warehouse-native/configuration/data-and-semantic-layer).
{% /callout %}
This guide steps through the basic concepts to help you set up essential product metrics in your Statsig project.
1. [How Metrics Work on Statsig](/metrics/how-metrics-work)
2. [Raw Events](/metrics/raw-events)
* [Types of Raw Events](/metrics/raw-events#types-of-raw-events)
* [Unit Identifiers](/metrics/raw-events#unit-identifiers) required for raw events
* [ID Mapping Considerations](/experiments/create-new#id-mapping-capabilities) for cross-ID analysis
* [Ingesting Raw Events](/metrics/raw-events#ingesting-raw-events)
* [Seeing Raw Events in the Statsig Console](/metrics/raw-events#raw-events-in-console)
3. [Auto-generated Events](/metrics/raw-event-metrics)
* [Event Count](/metrics/raw-event-metrics#event-count-metric) and [Event DAU](/metrics/raw-event-metrics#event-dau-metric) metrics
* [User Accounting](/metrics/raw-event-metrics#user-accounting-metrics)
4. [Custom Metrics](/metrics/custom-metrics)
5. [Importing Precomputed Metrics](/metrics/precomputed-metrics)
6. [Pulse Metrics](/metrics/pulse)
{% callout type="info" %}
**Ask for Help**: Hop on to the [Statsig Slack channel](https://statsig.com/slack) if you have any questions or want to validate the best path to import your metrics.
{% /callout %}
================================================================================
# How Metrics Work on Statsig
URL: https://docs.statsig.com/metrics/how-metrics-work
Updated: 2026-07-11
================================================================================
## How metrics work on Statsig
{% callout type="info" %}
**Warehouse Native users**
: You're viewing the Cloud docs for this page. Metrics and experiments behave differently in Warehouse Native. Read more in
[About Warehouse Native](/statsig-warehouse-native/introduction#how-warehouse-native-works)
.
{% /callout %}
A metric in Statsig is a numeric value for each user on a given day. You can aggregate this value across the entire user base or a subset, such as the test or control group of an experiment.
For example, say one user made two purchases on September 1st, and another made only one. You can aggregate these values across multiple users to calculate the total number of purchases across all users on September 1st.
By default, Statsig computes metrics from logged raw events in the production environment.
When testing experiments in lower environments (such as development or staging) with **Enable for Environments**, you can track cumulative exposures and metric results from those environments. Tracking lower-environment data lets you validate your experiment setup before launching to production. Statsig prioritizes production data for final *Pulse* result analyses.
## Sources of Statsig metrics
There are two fundamental sources of metrics in Statsig:
1. **Raw Events**
* Statsig [auto-generates certain metrics](/metrics/raw-event-metrics) such as **event\_count** and user accounting metrics from these events
* You can also define [custom metrics](/metrics/create) using your logged raw events
2. **Precomputed Metrics** - You can provide these pre-computed values to Statsig
Statsig's Stats Engine joins these metrics with your exposure events from feature gates and experiments to compute experiment results and analytics.
{% figure %}

{% /figure %}
{% callout type="info" %}
**How are events and metrics billed?**
Statsig bills each event (or a row when importing from your data warehouse) once, regardless of how many experiments use the event.
{% /callout %}
================================================================================
# Raw Events
URL: https://docs.statsig.com/metrics/raw-events
Updated: 2026-07-11
================================================================================
Statsig uses the raw events emitted by your application to compute a wide range of product metrics. These events contain the context needed to understand user behavior and infer user intentions.
## Types of raw events
Statsig records two types of raw events from your application:
1. **Exposure events** track which users are assigned to control and test groups. This data allows Statsig to generate test results so you can evaluate the impact of new features and experiments. Exposure events also allow Statsig to assess the health of an experiment so you can make key decisions based on trustworthy data. Statsig requires exposure events at a minimum to generate experiment results.
2. **Custom events** track user actions and any events triggered during use of your application, including events that capture performance (for example, latency) or analytics data (for example, session start). These events allow Statsig to assess overall user engagement in your application (for example, daily active users, weekly stickiness) and changes in user behavior as you roll out new features and experiments.
{% callout type="note" %}
When logging custom events, avoid using dot (.) notation in metadata keys. Keys containing dots are interpreted as nested paths during JSON parsing (e.g., by JSON\_VALUE), which can cause the values to be parsed as NULL.
{% /callout %}
## Unit identifiers
You must include at least one unit identifier when you record any raw events with Statsig. The unit identifier is required for two reasons:
1. To ensure that your users receive a consistent application experience when allocated to control or test groups in an experiment
2. To join exposure events with all custom events triggered by a given user to compute experiment results
## Ingesting raw events
You can send raw events into Statsig in three ways.
{% figure %}

{% /figure %}
1. Integrate with Statsig's [client](/client/introduction) or [server](/server/introduction) SDKs or [HTTP](/http-api/overview) API
2. Set up Statsig as a destination in a data connector such as [Segment](/integrations/data-connectors/segment#configuring-incoming-events), [mParticle](/integrations/data-connectors/mparticle#configuring-incoming-events), [RudderStack](/integrations/data-connectors/rudderstack#configuring-incoming-events) and [Census](/integrations/data-connectors/census#configuring-incoming-events)
3. Import from your data warehouse such as [Snowflake](/integrations/data-imports/snowflake#direct-ingestion-from-snowflake), [BigQuery](/integrations/data-imports/bigquery), and [Redshift](/integrations/data-imports/redshift#direct-ingestion).
{% callout type="note" %}
When processing events, Statsig drops event names that contain this regex/character set:
`"\\[\]{}<>#=;&$%|\u0000\n\r`
{% /callout %}
## Raw events in console
As you ingest custom events, they appear in the *Metrics* section under the **Events** tab in the Statsig console.
{% figure %}

{% /figure %}
You can toggle between a list view or chart view of your events to view the trend line over time.
{% figure %}

{% /figure %}
Statsig provides two unit identifiers by default: **User ID** and **Stable ID**. Select a unit identifier from the dropdown to view all events that include that unit identifier. Go to the [guide to create custom ID types](/guides/experiment-on-custom-id-types#step-1---add-companyid-as-a-new-id-type-in-your-project-settings) to create additional unit identifiers for your project.
{% figure %}

{% /figure %}
## Billing
Statsig bills for the two types of raw events described above. Statsig bills only for production environment events.
1. Statsig records an **Exposure Event** for billing when you check a user for assignment in a Feature Gate or Experiment, or check for a value using a Dynamic Config.
The following checks are not billed:
* Statsig doesn't bill you for duplicate checks for the same user on the same Feature Gate, Experiment, or Dynamic Config within an hour.
* Statsig doesn't bill you for checks against Features Gates that are disabled.
* Statsig also doesn't bill you for checks for users who are in not participating in an experiment due to the allocation or targeting you have configured.
2. Statsig records a **Custom Event** for billing when you log an event using the Statsig SDK (or import from your data warehouse, or ingest from your data collector). Each event may contain multiple unit identifiers and may be used in multiple experiments.
* Statsig bills custom events only once, regardless of the number of experiments in which they are used.
================================================================================
# Auto-generated Metrics
URL: https://docs.statsig.com/metrics/raw-event-metrics
Updated: 2026-07-11
================================================================================
Metrics are critical for monitoring the health and usage of your product and the impact of new features and experiments.
Statsig automatically generates an "event\_count" metric for each uniquely named **custom event** that you log. Statsig creates auto-generated metrics from production environment events, and creates an event\_count metric for any newly logged custom event within 24 hours of the first logged events.
When testing experiments in lower environments (such as development or staging) with **Enable for Environments**, you can track cumulative exposures and metric results collected from those environments. This allows you to validate your experiment setup before launching to production. Statsig prioritizes production data for final Pulse result analyses.
This auto-generated metric consists of three elements:
1. **Roll-up Window** - Statsig computes metrics from custom events aggregated over a 24-hour day, with the hours depending on your company's setting. These hours don't change with daylight saving time. This prevents some days from having 23 and 25 hours which can cause a +/-4% change to some metrics on a biannual basis.
2. **Unit Identifier** - While you can record custom events with and without a unique user identifier, Statsig requires a unit identifier (usually a user\_id) to track a user across multiple events and sessions to support Experiments, Pulse (experiment results), and Autotune. If you don't have access to a user\_id when logging a custom event, create a temporary identifier to track users at a session or device-level.
3. **Metric Value** - Statsig automatically computes values for **event\_count**, which measures the number of times an event is triggered.
Through October 16, 2024, Statsig also auto-computed values for an **event\_dau** metric that measures the number of unique users who triggered the event. Statsig no longer auto-computes an **event\_dau** metric for every logged event. You can create metrics that function like **event\_dau** through [Custom Metrics](/metrics/custom-dau). Go to the [event\_dau deprecation details](/metrics/deprecate-event-dau) for more information.
| Metric | Automatic | Dimensions | Possible Values | Description | Example |
| ----------- | --------- | ---------- | -------------------------- | -------------------------------------------------------------------------- | ------------------------------ |
| event\_count | Yes | Yes | 0, 1, 2,... | Counts the number of events triggered on a given day | Number of page views |
| event\_dau (Legacy support as of October 16, 2024) | Yes | Yes | 0, 1 | Marks each user as 1 or 0 based on whether they triggered the event or not | Unique users who viewed a page |
When you select an event type in the **Events** tab, a detailed view of the event appears, including any metrics linked to that event. Select a metric to go to its detail page.
{% figure %}

{% /figure %}
## Event count metric
Event count is the simplest metric in your Statsig project. For every event recorded, Statsig automatically creates an **event\_count** metric based on the number of times Statsig receives that event each day. In experiments, Statsig calculates this value for each user. Each user can have values of 0, 1, 2, and so on.
You can find an **event\_count** metric for each event type that you record in the Statsig console. The metric name matches the name of the raw event, and the metric type is marked as **event\_count**.
{% figure %}

{% /figure %}
## User accounting metrics
Statsig automatically derives **User Accounting** metrics based on any exposure or custom event triggered by a user on a given day.
**User Accounting** metrics start with a definition of a daily active user (DAU). By default, Statsig considers a user a DAU if they trigger any event, gate check, or experiment check on a given day. A DAU is a binary designation assigned to every user: a user is either a DAU for a given day or not (inactive). You can customize this DAU definition to exclude or include specific exposure and custom events from your application.
| Metric | Automatic | Dimensions | Possible Values | Description | Example |
| ----------- | --------- | ---------- | -------------------------- | -------------------------------------------------------------------------- | ------------------------------ |
| user | Yes | No | Depends on specific metric | Counts users that trigger any exposure or custom event on a given day | Daily Active Users |
Like the **event\_dau** metric (deprecated as of October 16, 2024), Statsig computes **User Accounting Metrics** for each unit ID you define in your Statsig project. For example, given User IDs, DAU counts the number of distinct users that triggered the event. Given Stable IDs, DAU counts the number of distinct devices running your application.
Go to [User Accounting Metrics](/metrics/user) for the full list of user accounting metrics and information about customizing the DAU definition.
Statsig's default day starts at GMT-8 (Pacific Standard Time), and doesn't follow daylight savings time.
{% callout type="note" %}
Auto-generated
**User Accounting Metrics**
aren't supported today for data warehouse ingestions.
{% /callout %}
## Metrics catalog
The **Metrics Catalog** tab allows you to search and tag your metrics. Tags organize metrics into collections. For example, you can tag metrics focused on a product area, business function, or business objective. You can also create a guardrail metrics collection that teams add to every experiment to confirm there are no unexpected effects in other parts of the business. After you create a tagged collection, you can filter to that set when viewing experiment results.
{% figure %}

{% /figure %}
## Event DAU metric (legacy support only)
{% callout type="warning" %}
Refer to the deprecation notice above.
{% /callout %}
Like **event\_count**, Statsig formerly created an **event\_dau** metric that measures the number of unique users who trigger a specific event on a given day. Each user receives a value of 1 (active) or 0 (inactive) based on whether they triggered an event on that day.
An **event\_dau** metric produces a single value per user per day. When aggregated across users for the duration of an experiment, the result is known as the "Event Participation Rate," which represents the probability that a unit is DAU for that event. **event\_dau** metrics are always between 0 and 1 for a user, because they are computed as "# Days with the Event" / "# Days Being Considered".
{% callout type="info" %}
**Tip**
: Sometimes you might want a metric similar to
**event_dau**
but not normalized by a number of days. If you're looking for a metric that measures if the user has an event over the entire duration of the experiment, try a custom metric set to metric type "Unit Count" with "One-Time Event" rollup mode.
{% /callout %}
This metric works well in experimentation as it minimizes outliers, has tighter confidence intervals, and enables a simple measure to describe a user's breadth of activity across different events.
Statsig computes the **event\_dau** for each unit ID you define in your Statsig project. For example, given User IDs, **event\_dau** counts the number of distinct users that triggered the event. Given Stable IDs, **event\_dau** counts the number of distinct devices using your application.
You can find an **event\_dau** metric for each event type that you record with Statsig. The metric name matches the name of the raw event, and the metric type is marked as **event\_dau**.
{% figure %}

{% /figure %}
================================================================================
# Deprecating Event_dau Metric
URL: https://docs.statsig.com/metrics/deprecate-event-dau
Updated: 2026-07-11
================================================================================
### Deprecation details
From Wednesday, October 16, 2024, Statsig stopped auto-generating new event\_dau metrics for incoming events. Statsig continues to auto-generate an event\_count metric for each logged event.
{% callout type="note" %}
This change only affects Statsig Cloud customers. This change doesn't affect Warehouse Native customers.
{% /callout %}
* This change doesn't affect any existing event\_dau metrics that you have used in a gate, experiment, dashboard, or other Custom Metrics.
* Existing event\_dau metrics that were archived or never used in another config no longer exist. To retain these metrics, refer to the Next steps section below.
* Going forward, you must create new event\_dau metrics manually as a Custom Metric. Refer to [this guide](/metrics/custom-dau) to learn how to create a DAU metric.
Contact Statsig support if you have questions or concerns about this change.
### Motivation for this change
Historically, Statsig auto-generated an event\_count and event\_dau metric for every incoming event. Auto-generating two metrics for every event causes confusion and clutter inside projects. This change produces a cleaner Metrics Catalog and faster Console performance, while retaining the ability to create event\_dau metrics for specific events.
### Next steps
To keep any unused event\_dau metrics, mark them as active by performing any of the actions below:
* Adding a Tag (RECOMMENDED)
* Adding a description
* Referencing in a gate/experiment/dashboard
These actions mark your unused metric as active, indicating that Statsig shouldn't deprecate the metric.
================================================================================
# Custom Metrics
URL: https://docs.statsig.com/metrics/custom-metrics
Updated: 2026-07-11
================================================================================
## Custom metrics
You can create **Custom Metrics** using the custom events you ingest by filtering or aggregating events based on event metadata. Include metadata along with the custom events when you log these events with Statsig.
For example, in addition to tracking overall **event\_count** for all events of type *purchase\_event*, you may want to filter these metrics only for events where your users purchase a specific product category.
Statsig supports six types of custom metrics:
| Metric Type | Description | Examples |
| ----------- | -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| Event Count | **Total count of events** filtered by the *Value*, *Metadata*, or *User* properties of an event type | **Add to Cart** event filtered by category type |
| User Count | **Number of unique users** that trigger events filtered by the *Value*, *Metadata*, or *User* properties of an event type | **Active Users** based on their views of a product category |
| Aggregation | **Sum or Average** of the *Value*, *Metadata*, or *User* property of an event type | **Total Revenue** |
| Ratio | **Rates** (e.g. cart conversion rate, purchase rate), **Normalized Values** (e.g. sessions per user, items per cart) | **Cart Conversion Rate**, **Sessions per User** |
| Funnel | **Funnels**- funnel of multiple events with conversion tracking | **Sign-up Funnel**, **Checkout Funnel** |
| Count Distinct | **Count Distinct**- Number of unique values for a given field, often approximated with sketches | **Unique Songs** listened per user |
{% callout type="info" %}
Sketch-based count distinct metrics are in Early Access. Reach out to Statsig Support to request access.
{% /callout %}
The "average" in aggregation is the average of event value (average revenue per purchase per user), not the average of exposed units (average revenue per user). The latter is defined by sum.
Refer to the **Metrics 201** topic, [Creating Custom Metrics](/metrics/create), to learn how to create custom metrics for your product.
================================================================================
# Precomputed Metrics
URL: https://docs.statsig.com/metrics/precomputed-metrics
Updated: 2026-07-11
================================================================================
## Importing precomputed metrics
### Importing precomputed metrics from your data warehouse
Statsig integrates natively with cloud data warehouses such as [Snowflake](/data-warehouse-ingestion/snowflake), [BigQuery](/data-warehouse-ingestion/bigquery), and [Redshift](/data-warehouse-ingestion/redshift) to ingest your existing metrics for computing experiment results. Go to [Data Warehouse Ingestion](/data-warehouse-ingestion/introduction) to get started.
## Debugging precomputed metrics
Statsig creates a metric detail page for all precomputed metrics imported from your data warehouse. These metric detail pages take a few hours to generate after import or ingestion. The fastest way to start viewing and debugging your precomputed metrics is through the **Metrics Logstream** in the **Metrics Catalog** tab within **Metrics**.
{% figure %}

{% /figure %}
The **Metrics Stream** surfaces all ingested, precomputed metrics in real time, enabling you to check metric name, metric value, unit identifier, ID type, and ingestion date.
{% callout type="info" %}
**Tip**
: A common issue is ensuring that precomputed metrics have the correct ID type. Pay extra attention to this column.
{% /callout %}
The **Metrics Stream** only appears if you are actively ingesting precomputed metrics. If it doesn't appear at the bottom of your **Metrics Catalog**, Statsig likely isn't receiving your precomputed metrics due to a connection issue or an invalid schema.
================================================================================
# User Property
URL: https://docs.statsig.com/metrics/user-property
Updated: 2026-07-11
================================================================================
Breaking down Pulse results by user properties (such as Free vs Paid or OS type) lets you slice data by those properties.
You set user properties when you create the User object used with the Statsig SDK. Statsig freezes these properties when a user is first exposed to a feature gate or experiment, because the experiment may change those properties (for example, converting a Free user to Paid).
{% figure %}

{% /figure %}
You can run custom queries on your Pulse results in the **Explore** tab to group by or filter by user properties.
{% figure %}

{% /figure %}
================================================================================
# Event Property
URL: https://docs.statsig.com/metrics/metric-dimensions
Updated: 2026-07-11
================================================================================
Statsig enables you to break down metrics into a single set of non-overlapping dimensions for detailed analysis.
For example, you can break down an event such as **add-to-cart** into product categories such as *sports*, *toys*, *appliances*, *electronics*. To do this, log **add-to-cart** events and provide the product category in the event's **value** field. Refer to the [Statsig SDK reference guide](/client/javascript-sdk#event-logging) to learn more.
Statsig enables you to define up to four custom dimensions for an event (one through the **value** field and three through **metadata** fields). To configure these custom event fields, go to *Metrics > Events*, select the event you want to configure, and then go to the **Setup** tab for that event.
{% figure %}

{% /figure %}
Providing custom dimensions with logged events allows you to break down the impact on the total **add-to-cart** events by category in Pulse, as shown below. This breakdown lets you identify the category most impacted by your experiment.
{% figure %}

{% /figure %}
Statsig recommends keeping the number of distinct dimensions in your logging below 8. Tracking dimensions involves additional computational and storage resources, so Statsig prioritizes notable dimensions that make up a significant portion of your traffic. Statsig tracks dimensions that make up more than 5% of your total event volume. Lower-frequency dimensions generally have more statistical noise and wider confidence intervals, and Statsig buckets these as "Other".
Statsig also supports metric dimensions for custom metrics that are **Aggregations**. To set this up, log your dimension as a **value** and the number to be tallied as a metadata field.
This is the old metric dimension page. Statsig renamed it to Event Property.
================================================================================
# Ingesting Metrics
URL: https://docs.statsig.com/metrics/ingest
Updated: 2026-07-11
================================================================================
Statsig can ingest your precomputed product and business metrics using the data warehouse connector (Metrics Imports). Integrations with [Snowflake](/integrations/data-imports/snowflake), [BigQuery](/integrations/data-imports/bigquery), and [Redshift](/integrations/data-imports/redshift) are supported.
Statsig doesn't automatically process these metrics until you mark them as ready, because you might load data out of order. After you finish loading data for a period, mark the data as ready by calling the `mark_data_ready` API.
```
curl --location --request POST ‘https://api.statsig.com/v1/mark_data_ready’ \
--header ‘statsig-api-key: {your statsig server secret}’ \
--header ‘Content-Type: application/json’ \
--data-raw ‘{
“timestamp”: 1647975283,
“type”: “metrics”
}
```
The timestamp provided should be:
* A unix timestamp
* The latest point in time for which all metrics have been uploaded
* Any future calls to this API with an earlier timestamp are invalid
* Statsig doesn't guarantee correct behavior if you provide metrics with an earlier timestamp after calling this API
Statsig processes metrics as a full day in the PST timezone, and waits until a full day is marked as ready before processing that day.
## Debugging precomputed metrics
All precomputed metrics generate Metric Detail View pages. These Detail View pages take a few hours to generate after ingestion. The fastest way to start viewing and debugging your precomputed metrics is through the **Metrics Logstream** on the **Metrics Catalog** tab within **Metrics**.
{% figure %}

{% /figure %}
The **Metrics Logstream** surfaces all ingested, precomputed metrics in real time, enabling you to check metric name, value, ID, ID type, and ingestion date. A common issue is ensuring precomputed metrics have the correct ID type, so pay extra attention to this column.
You can also include test metrics tagged with **isTest**, which you can toggle on or off for debugging in the **Metrics Logstream**. The **isTest** flag is only available for precomputed metrics ingested through Statsig's APIs. Support for this flag through integrations with Snowflake, BigQuery, and Redshift is coming soon.
To mark a batch of metrics as test metrics, set the `isTest` parameter to `true` in the request data, as shown below.
```bash
curl \
“https://events.statsigapi.net/v1/log_custom_metric” \
--header “statsig-api-key: ” \
--header “Content-Type: application/json” \
--request POST \
--data “{"isTest": true, “metrics": [{"user_id": "1237", "metric_name": "test_metric", "id_type": "user_id", "metric_value": 90}, {"user_id": "4568", "metric_name": "ratio", "id_type": "stable_id", "numerator": 3, "denominator": 15}]}”
```
{% figure %}

{% /figure %}
The **Metrics Logstream** only appears if you are actively ingesting precomputed metrics. If it doesn't appear at the bottom of your **Metrics Catalog**, a connection or schema issue may be preventing Statsig from receiving your precomputed metrics.
================================================================================
# Pulse Metrics
URL: https://docs.statsig.com/metrics/pulse
Updated: 2026-07-11
================================================================================
Experiments with Statsig use **Pulse** to compute and communicate results. The metric type affects how Statsig computes and interprets the final result.
Statsig aggregates most metric types across all users in the group. For metric types that use ratios, Statsig aggregates only across [participating users](/experiments/interpreting-results/participating-units) (users that have a non-null value for that metric). This article covers the metric types available in experiments and how to interpret their Pulse results.
## Pulse statistics by metric type
| Metric Type | Total | Mean | Units |
| ------------------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
| event\_count | Sum of events (99.9% winsorization applied) | Average events per user (99.9% winsorization applied) | All users |
| event\_dau | Sum of event DAU (distinct user-day pairs) | Average event\_dau value per user per day. This is called "Event Participation Rate" because it represents the probability a user is DAU for that event. | All users |
| sum | Total sum of values (99.9% winsorization) | Average value per user (99.9% winsorization) | All users |
| mean | Overall mean value | Overall mean value | [Participating users](/experiments/interpreting-results/participating-units) |
| event\_user | Count of distinct users that have had the event. | Average metric value per user per day. Depending on Rollup Mode, can be a one-time event or daily participation rate. | All users |
| ratio | Not shown | Overall ratio: sum(numerator values)/sum(denominator values) | [Participating users](/experiments/interpreting-results/participating-units) |
| funnel | Not shown | Overall ratio: sum(numerator values)/sum(denominator values) | [Participating users](/experiments/interpreting-results/participating-units) |
| user: dau, wau, mau\_28day | Not shown | Average metric value per user per day. The probability that a user is xAU | All users |
| user: new\_dau, new\_wau, new\_mau\_28day | Count of distinct users that are new xAU at some point in the experiment | Fraction of users that are new xAU | All users |
| user: retention metrics | Overall average retention rate | Overall average retention rate | [Participating users](/experiments/interpreting-results/participating-units) |
| user: L7, L14, L28 | Not shown | Average L-ness value per user per day | All users |
| count\_distinct | Total number of unique values | Average number of unique values per user | All users |
**Some example metric breakdowns in Pulse:**
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
## Event count and event DAU in Pulse
{% callout type="warning" %}
**event_dau Legacy Support**
: event_dau metrics are now in legacy support only and are no longer created for new events. Existing event_dau metrics will continue to be available for any of your new experiments and will continue to be computed daily. For all new events, you should create an event_user metric to measure daily active users.
{% /callout %}
From [Metrics 101](/metrics/101) and [Auto-generated Metrics](/metrics/raw-event-metrics),
* [**event\_count**](/metrics/raw-event-metrics#event-count-metric) measures the volume of the activity based on count of events triggered
* [**event\_dau**](/metrics/raw-event-metrics#event-dau-metric) measures unique daily users who triggered a given event
For example, the table below shows the **event\_count** and **event\_dau** metrics for two event types, *Page Views* and *Add to Cart*, for three users over three days.
{% figure %}

{% /figure %}
Over the duration of an experiment, Pulse results measure the change in:
* the **mean** event\_count, or the average event count per user
* the **mean** event\_dau, or average active days per user: Statsig calls this the **Daily Event Participation Rate**
For example, the table below shows the **Total event\_count**, **Total Units**, and **Mean event\_count** over the same three days as above, now in the context of an experiment.
{% figure %}

{% /figure %}
The table below shows the **Total event\_dau**, **Total Units**, and **Mean event\_dau** over the same three days of the experiment. Alice was active on three days for the *Page View* event and on one day for the *Add to Cart* event. Alice's average event\_dau is 3/3 for the *Page View* event and 1/3 for the *Add to Cart* event. Alice's **daily participation rate** is 1.00 for the *Page View* event and 0.33 for the *Add to Cart* event. Statsig aggregates the average event\_dau for each user in the experiment, with each user weighted equally.
{% figure %}

{% /figure %}
To measure engagement changes for a call-to-action link or button, use event\_count to measure the change in average clicks per user, and use event\_dau to measure the change in the daily participation rate for the click.
{% callout type="info" %}
**Event Count and Event DAU in Custom Metrics**
: When creating a custom ratio metric, use event_count to include all events (counting all events triggered by the same user). Use event_user (or event_dau, if available) to count unique active users on a given day (all events triggered by the same user are counted as one).
{% /callout %}
## Winsorization
To reduce the impact of outliers, Statsig caps *event\_count* and *sum* metric types at the 99.9th percentile by default. This reduces the risk that bots and extreme values skew experiment results.
The 99.9th percentile threshold is computed using all non-zero and non-null metric values. All values exceeding this limit are replaced with the threshold value.
Warehouse Native (WHN) allows for more customization of winsorization by metric and by percentile.
## Frequently asked questions
**1. Can a ratio metric move in the opposite direction from both the numerator and denominator metrics?**
Yes, the ratio can rise while both the numerator and denominator metrics decline. This happens when the denominator falls more than the numerator. Statsig recommends tracking the numerator and denominator as independent metrics when monitoring a ratio metric. Ratio metrics are often subject to statistical noise and can be difficult to use for obtaining a statistically significant result.
**2. For ratio metrics, how does Statsig determine *participating users*?**
Statsig computes ratio metrics only for users with a non-zero value in the denominator. A user must trigger the denominator event on a given day to be included in the daily ratio. Statsig excludes users who don't trigger the denominator event during an experiment from the test vs. control comparison.
**3. What is the difference between One-Time Event and Daily Participation Rate?**
This distinction is relevant only in the context of an experiment. Daily participation rate counts the number of *days* a user has that event, divided by the number of *days* the user has been in the experiment. One-time event is a binary metric that checks whether the user has that event *at least once* during the experiment.
================================================================================
# Local Metrics
URL: https://docs.statsig.com/metrics/local-metrics
Updated: 2026-07-11
================================================================================
## How Local Metrics work
Local Metrics are metrics scoped to an individual config (a specific experiment or gate). Statsig creates them within that config to capture how the metric trends in that context, without adding the metric to the project-wide Metrics Catalog.
## Creating a Local Metric
You can create a Local Metric from two places within your config:
1. **Setup**: While setting up your gate or experiment and adding Primary, Secondary, or Monitoring metrics, you can add a metric that doesn't yet exist to your Scorecard. Select **+ Create New Local Metric** to enter the Local Metric creation flow.
{% figure %}

{% /figure %}
2. **Pulse**: After you start your gate or experiment rollout, you can still create and add a new Local Metric to your scorecard. From your Scorecard in Pulse, select **Edit Primary Metrics** (or Secondary or Monitoring metrics depending on where you want to add your new Local Metric), and then **+ Create New Local Metric**.
{% figure %}

{% /figure %}
Entering the Local Metric creation flow from either entry point starts the Local Metric creation wizard. The wizard lets you select events, add filters, and preview the output values.
{% figure %}

{% /figure %}
When you are ready to save your Local Metric, you can choose to save it to either the Primary/ Secondary Metrics section of your Scorecard (for experiments), or the Monitoring Metrics section of your feature gate rollout.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
After you create a Local Metric, you can select it in your Scorecard to view its configuration in the Local Metric wizard.
## Types of Local Metrics
The types of Local Metrics you can create are similar to Custom Metrics (accessible through the *Metrics* tab), with a few exceptions.
Here are the supported types of Local Metrics:
| Metric Type | Description | Examples |
|-------------|-----------------------|---------|
| Event Count | **Total count of events** filtered by the *value* and *metadata* properties of an event type | **Add to Cart** event filtered by category type |
| User Count | **Number of unique users** that trigger events filtered by the *value* and *metadata* of an event type| **Active Users** based on their views of a product category |
| Aggregation | **Sum or Average** of the *value* of an event type | **Total Revenue** |
| Ratio | **Rates** (e.g. cart conversion rate, purchase rate), **Normalized Values** (e.g. sessions per user, items per cart) | **Cart Conversion Rate**, **Sessions per User** |
The one type of Custom Metric that you can't (yet) create as a Local Metric are funnels.
## Lifecycle of Local Metrics
By default, Local Metrics are scoped to the config they are created in and exist only for the lifecycle of that config. After you make a decision on your experiment or launch your feature gate, Statsig no longer computes the Local Metric.
Local Metrics don't appear in your *Project Metrics Catalog* and aren't searchable in top-line search.
Converting a Local Metric into a Metrics Catalog metric isn't yet supported, but this conversion flow is coming soon. In the meantime, you can recreate the same metric definition as a Custom Metric in the *Metrics Catalog* if you want the metric to exist outside the scope of your gate or experiment.
================================================================================
# Metrics Dashboard
URL: https://docs.statsig.com/metrics/console
Updated: 2026-07-11
================================================================================
Metrics are available for all unit types enabled in the project. Statsig provides User ID and Stable ID by default; you can add others following [these steps](/guides/experiment-on-custom-id-types#step-1---add-companyid-as-a-new-id-type-in-your-project-settings). Select a unit type from the dropdown to view event DAU and user accounting metrics calculated for that unit type.
{% figure %}

{% /figure %}
## Events
The Metrics console allows you to visualize all the events that you have logged in Statsig. The **Events** tab shows all the events, including a real-time stream of events as they come in.
{% figure %}

{% /figure %}
You can toggle between a list view or chart view of your events to view the trend line over time.
{% figure %}

{% /figure %}
From here you can drill into each event to see a detailed view of the logs, broken down by each unique value that was logged.
{% figure %}

{% /figure %}
## Metrics Catalog
The **Metrics Catalog** tab lets you search and tag your metrics, as well as [create custom metrics](/metrics/create). Tags let you organize metrics and create collections grouped by product area, business function, business objective, or any other common attribute. You can also create a collection of guardrail metrics that teams check in every experiment to detect unexpected effects in other parts of the business. After you create a tagged collection, you can filter your experiment results to that collection and focus on the metrics that matter.
{% figure %}

{% /figure %}
Similar to the **Events** tab, you can toggle between a list view or chart view of your metrics to view the trend line over time.
{% figure %}

{% /figure %}
================================================================================
# Metrics 201 - Creating Custom Metrics
URL: https://docs.statsig.com/metrics/201
Updated: 2026-07-11
================================================================================
In this guide, you'll learn to create your own metrics and organize them as your project grows. You'll also be able to customize the definition of your daily active users (DAU).
1. [Creating Custom Metrics](/metrics/create) for your product in the Statsig console
2. [Creating Metric Tags](/metrics/create-metric-tags) to organize the metrics in your Statsig Project
3. [Customizing the DAU Definition](/metrics/user) that Statsig uses to compute [User Accounting](/metrics/raw-event-metrics#user-accounting-metrics) metrics.
================================================================================
# Creating Custom Metrics
URL: https://docs.statsig.com/metrics/create
Updated: 2026-07-11
================================================================================
Statsig computes custom metrics from your raw events. To create custom metrics, navigate to **Metrics** from the left-hand navigation panel, then to the **Metrics Catalog** tab. Select the **Create** button.
{% figure %}

{% /figure %}
Statsig supports six types of custom metrics:
| Metric Type | Description | Examples |
| -------------- | -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| Event Count | **Total count of events** filtered by the *Value*, *Metadata*, or *User Object* properties of an event type | **Add to Cart** event filtered by category type |
| User Count | **Number of unique users** that trigger events filtered by the *Value*, *Metadata*, or *User Object* of an event type | **Active Users** based on their views of a product category |
| Aggregation | **Sum or Average** of the *Value*, *Metadata*, and *User Object* property of an event type | **Total Revenue** |
| Ratio | **Rates** (e.g. cart conversion rate, purchase rate), **Normalized Values** (e.g. sessions per user, items per cart) | **Cart Conversion Rate**, **Sessions per User** |
| Funnel | **Funnels**- funnel of multiple events with conversion tracking | **Sign-up Funnel**, **Checkout Funnel** |
| Count Distinct | **Count Distinct**- Number of unique values for a given field, often approximated with sketches | **Unique Songs** listened per user |
Statsig computes custom metrics daily for your **Metrics** dashboard, and rolls them up for the duration of the experiment in **Pulse Results** for your Feature Gates and Experiments. After you create a custom metric, it doesn't populate until the next day and doesn't backfill to previous days. Statsig calculates the metric only from the creation date forward.
Statsig lets you set a daily **max cap** for specific metrics. You can define maximum caps for various unit types associated with a metric. Statsig automatically adjusts any value exceeding the cap down to match it. For example, if purchases over $10,000 per day on your e-commerce platform shouldn't skew analysis, Statsig adjusts any transaction above that threshold down to the limit. Capped metrics are available for event count and aggregation (sum) metric types.
When creating a custom metric, you can preview the metric’s value at the bottom of the page. Select **View Output Preview** to see what the metric would have looked like based on its component values over the last 7 days, in both chart and table form.
{% figure %}

{% /figure %}
## Examples
### 1. Event count metrics
This example sets up a custom event metric to count the **number of add\_to\_cart events** filtered by a metadata property called *value*, which carries the price of the item added to the cart. Because the ID Type is set to *userID*, Statsig computes this metric as part of the test group that Statsig assigns the corresponding user to in an experiment.
{% figure %}

{% /figure %}
If you select the ID Type as *stableID*, Statsig computes this metric in the test group that Statsig assigns the corresponding device to in an experiment. When you select more than one ID Type, Statsig computes this metric for each ID Type you specify.
### 2. User count metrics
The example below creates a metric to count the **number of unique users** who viewed a product in the *toys* category that was priced under $10.
{% figure %}

{% /figure %}
**Time Window**
Statsig offers three time window options for user count metrics:
* **Daily Participation Rate**: Counts the total number of days a user has the selected event, divided by the number of days the user is in the experiment. The result is a value between 0 and 1, representing the probability of a user having the event on any given day. Works best for events expected to occur repeatedly for a given user.
* **One Time Event**: Checks if a user has the selected event at least once during their time in the experiment. The result is a binary metric with value 0 or 1 for each user. Ideal for events expected to occur only once per user, such as sign-up events.
* **Custom Attribution Window**: Lets you define a custom window after exposure to count an event toward a metric calculation.
### 3. Aggregation metrics
The example below shows a **Total Revenue** metric that sums the *value* associated with all purchase events.
{% figure %}

{% /figure %}
**\[Early Access] Currency Sum**
eCommerce customers often span multiple currency types and log payments in the currency the user actually pays. Currency sums convert payments recorded in different currencies to a single common currency. You must log two metadata fields in your event: a currency code and a value. Example metadata: `_{"currency_code": "USD", "currency_value": 123.45}_`
Statsig supports currency sums upon request. Ask in [Slack](https://statsig.com/slack).
### 4. Ratio metrics
The example below shows creating a **Cart Conversion Rate** metric. Statsig uses unique users who triggered the *purchase event* as the numerator, and unique users who triggered the *add to cart* event as the denominator. When calculating the numerator, Statsig includes only users who also had the denominator event on the same day. A user who has only a *purchase event* on a given day without an *add to cart* on that same day doesn't count toward the numerator.
{% figure %}

{% /figure %}
This pattern also applies to **click-through rates (CTR)** in any part of a step-wise product journey (funnels). Statsig recommends using unique users in both the numerator and denominator for these metrics. For example, a user who reloads a page multiple times but clicks only once corresponds to a 100% CTR (1 out of 1). A user who loads a page once but clicks a button multiple times also counts as 1 out of 1. This approach handles cases where users see a key button, such as "Sign-up," multiple times a day, and Statsig counts a single click as a success.
The example below shows creating an **Items per Cart** metric. You can track the number of unique items added to a cart by logging an *add\_to\_cart* event for each item. For the numerator, select total event count. For the denominator, select unique users. Because Statsig computes this metric daily and only for users with a non-zero denominator, it can generate ratios such as 1/1, 2/1, and 5/1 for individual users. When aggregated, this translates to an average such as 10/4 = 2.5 items per cart per day.
{% figure %}

{% /figure %}
{% callout type="info" %}
**A Word of Caution**
: In experimentation, ratio metrics are a frequent source of misleading information. It's possible to see an increase in
**click through rate**
alongside a net
*decrease*
in total clicks (the opposite may also happen). This situation can occur if the number of unique users viewing a button (denominator) decreases. As a best practice, Statsig recommends tracking the numerator and denominator as independent metrics when monitoring ratio indicator. Ratio metrics are often subject to statistical noise and can be tricky to use for obtaining a statistically significant result. In addition, for the numerator in ratios, Statsig excludes units that don't have a denominator value.
{% /callout %}
### 5. Funnel metrics
You can create a custom funnel metric from either the Custom Metrics Creation wizard in the Metrics Catalog or through the **Charts** tab.
{% figure %}

{% /figure %}
{% callout type="info" %}
**Important Note**
: Statsig handles funnel metrics differently between the Cloud and Warehouse Native platforms. Funnel metrics using the Cloud platform are
*always*
unordered, meaning that funnel steps can be completed in any order, and they have a time window of
*one day*
. This means that for the metric to record a completion, all steps must be triggered by a user within 24 hours. In contrast, funnel metrics on Warehouse Native have the option to be set with strict ordering and custom time windows.
{% /callout %}
### Components of funnel metrics
Funnel metrics have a few components:
1. **Lineage**: Surfaces the events used to generate the funnel
2. **Metric Value**: Metric value represents the overall funnel conversion rate, or the percentage of users who complete a funnel (trigger the end event) relative to all users who start the funnel (trigger the starting event)
3. **Conversion rate between stages**: This set of metrics track the percentage of users who triggered an event N relative to all users that triggered event N-1 in the funnel
{% figure %}

{% /figure %}
After Statsig creates and populates funnels, you can view your funnel metric like any other metric in Pulse. You can also expand the funnel metric to view Pulse performance at each step in the funnel.
In the example below, the **Square** variant shows a lift in the **overall funnel conversion rate**. Expanding the metrics to examine the entire funnel reveals two key insights:
* Both the **Square** and **Circle** variants show a lift in top-of-funnel DAU (*Land Page View Start DAU*). However, only the **Square** variant shows statistically significant increase in end-of-funnel DAU (*Purchase Event End DAU*).
* The overall funnel conversion rate improvement for **Square** is primarily due to the higher conversion from *Checkout Event* to *Purchase Event* stages in the funnel.
{% figure %}

{% /figure %}
### 6. Count distinct metrics
{% callout type="info" %}
Sketch-based count distinct metrics are in Early Access. Reach out to Statsig Support if you would like Statsig to enable this.
{% /callout %}
* **What it is:** A high-performance way to estimate the number of distinct values using compact sketch data structures. Example use cases include unique songs per user or unique products purchased per user.
* **Benefit:** Much faster to compute and uses significantly less memory compared to exact distinct counting, especially at high cardinality.
### Custom metrics and dimensions
For custom metrics composed of a single event (event count, event DAU, aggregation, and so on), Statsig automatically includes the dimensions of the source event in the custom metric and exposes them as dimensions of that metric. If your custom metric is composed of two or more different events, Statsig doesn't include the dimensions of those source events in the custom metric, and they aren't queryable through the "Explore" tab in Pulse Results.
================================================================================
# Custom DAU Metric Creation Guide
URL: https://docs.statsig.com/metrics/custom-dau
Updated: 2026-07-11
================================================================================
## Custom DAU metric creation guide
This guide covers the steps to create a custom DAU metric.
### Step 1: Navigate to the metrics catalog
Go to the *Metrics Catalog* in the left navigation bar and select **Create**.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
### Step 2: Name your metric
Enter a name for your metric. For example: "Add to Cart DAU".
{% figure %}

{% /figure %}
### Step 3: Choose the metric type
To create a DAU metric, select **Unit Count** as the metric type.
{% figure %}

{% /figure %}
### Step 4: Select the ID type
Select the ID type to count. For example, select **User ID**, which is the most common ID type for DAU metrics.
{% figure %}

{% /figure %}
### Step 5: Choose the event
Select the event to use for your metric. For example, select the **Add to Cart** event.
{% figure %}

{% /figure %}
### Step 6: Submit the metric
Submit the metric. Your custom DAU metric is now created.
{% figure %}

{% /figure %}
================================================================================
# Archiving and Deleting Metrics
URL: https://docs.statsig.com/metrics/archiving-metrics
Updated: 2026-07-11
================================================================================
Statsig offers two ways to manage the end-of-life for your metrics.
* **Archiving a metric**: Statsig stops computing the metric, but retains its history. Use archiving when a metric is no longer relevant but you want to preserve its historical record. For example, archive older versions of a metric that continues to evolve.
* **Deleting a metric**: Statsig removes the metric and its history completely. Use deletion when you made a mistake, logged or imported an irrelevant metric, or created a more accurate replacement. Examples include an incorrect definition, incorrect name, or a duplicate metric.
## Archiving metrics
### Archiving a metric
There are two ways to archive a metric:
1. In your Metric Catalog, select the metric(s) you want to archive to see a toolbar of options appear to **Archive**, **Compare**, or **Tag**. Select the **Archive** icon.
{% figure %}

{% /figure %}
2. In the Metrics Detail View page, select the "..." in the upper right-hand corner, and select **Archive**.
{% figure %}

{% /figure %}
After you select Archive, Statsig checks if this metric is used in any feature gates, experiments, or other metrics.
{% figure %}

{% /figure %}
Feature gate or experiment dependencies appear as soft warnings (no action required). Metric dependencies require you to remove the dependency before proceeding, because archiving a metric stops its computation and would affect other metrics that depend on it.
After you remove all metric dependencies, a 24-hour grace period begins during which you can undo the archival. The Metrics Detail View page shows a banner at the top indicating the start of the grace period.
After the 24-hour grace period, Statsig stops computing the metric. The Metric Detail View page updates with a banner confirming the metric is archived, and Metric Value changes to an inactive state.
### Implications of archiving a metric
*As soon as you click **Archive**,*
* A 24-hour grace period starts
* Owners of experiments and gates using this metric receive an email notification about potential impact
*After the 24-hour grace period has ended,*
* Statsig stops computing archived metrics.
* Archived metrics won't show up in your Metric Catalog search. To access all archived metrics, go to the last page(s) of your Metrics Catalog.
* Statsig removes archived metrics from Pulse, including any scorecard entries in experiments or Monitoring Metrics sections of Feature Gates
### Unarchiving a metric
If you mistakenly archived a metric, you can undo the archival.
* *During* the 24-hour grace period: Select "undo" on the archival banner at the top of the Metrics Detail View page. Because the metric is still being computed, unarchiving restores it to your Metrics Catalog and to any experiment results that include the metric.
{% figure %}

{% /figure %}
* *After* the 24-hour grace period: Either a) navigate to the last few pages of your Metrics Catalog, select the archived metric(s) you want to unarchive to display a toolbar of options, and select the **Unarchive** icon, or b) in the Metrics Detail View page of the archived metric, select **Unarchive** in the archival banner.
{% figure %}

{% /figure %}
Because the grace period has ended and metric computation has already stopped, the metric's calculation restarts from scratch and Statsig doesn't restore historical data.
### Auto-Archival
To reduce metric clutter, Statsig provides a default auto-archival feature that removes metrics not in use for at least 60 days. Metric creators and admins receive a warning about a week before archival. At that point, they can extend the metric for another 60 days or mark it as permanent. The entire process is outlined below:
{% figure %}

{% /figure %}
#### How does Statsig measure activity?
Statsig counts the number of times the custom metric is used in one or more of the following components:
1. *Scorecard*: Used in experiments, pulse reports, holdouts, etc.
2. *Dashboards*: Used to build dashboards and other analytical assets
3. *Other Metrics*: Used to calculate other composite metrics
If a metric is in use, Statsig considers it active. You can see a summary of a metric’s usage on the metric’s main page:
{% figure %}

{% /figure %}
Statsig also detects: (1) any edits to the metric, including changing setup fields or restoring a previous version; (2) adding tags to the metric; or (3) creating or modifying an alert on the metric. Any of these interactions restarts the 60-day clock.
#### How to pause / stop auto-archiving
Any tracked action (such as adding the metric to a scorecard) also removes the metric from the archival queue. To pause the archival process, extend the metric for another 60 days. You can also mark the metric as permanent, which removes it from the auto-archival process entirely. Reserve permanent status for your most important and widely reused metrics.
{% figure %}

{% /figure %}
You can also mark a metric as permanent by selecting the setup dropdown from the metrics page and choosing **Mark as Permanent**.
{% figure %}

{% /figure %}
To turn off auto-archiving entirely for your project, navigate to the Project Settings page.
{% figure %}

{% /figure %}
## Deleting metrics
### Deleting a metric
To delete a metric, navigate to the Metrics Detail View page of the metric, select the "..." in the upper right-hand corner, and select **Delete**.
{% figure %}

{% /figure %}
After you select Delete, Statsig checks if the metric is used in any feature gates, experiments, or other metrics. Feature gate or experiment dependencies appear as soft warnings (no action required). Metric dependencies require you to remove the dependency before proceeding, to avoid breaking other metrics that depend on it.
{% figure %}

{% /figure %}
After you remove all metric dependencies, a 24-hour grace period begins during which you can undo the deletion. The Metrics Detail View page shows a banner at the top indicating the start of the grace period.
{% figure %}

{% /figure %}
***Metric deletion can't be undone after the grace period.***
### Implications of deleting a metric
*As soon as you click **Delete**,*
* A 24-hour grace period starts
* Owners of experiments and gates using this metric receive an email notification about potential impact
*After the 24-hour grace period has ended,*
* Statsig permanently removes deleted metrics and their history, and you can't restore them.
* Statsig removes deleted metrics from Pulse, including any scorecard entries in experiments or Monitoring Metrics sections of Feature Gates
================================================================================
# Creating Metric Tags
URL: https://docs.statsig.com/metrics/create-metric-tags
Updated: 2026-07-11
================================================================================
## Tagging metrics
To create a metric tag, click on **Metrics** in the left hand navigation menu, and click on the [Metrics Catalog](https://console.statsig.com/4TLCtqzctSqusYcQljJLJE/metrics/metrics_catalog) tab.
{% figure %}

{% /figure %}
In the **Metrics Catalog** tab, click on the **Tags** filter icon in the upper right-hand corner, and then tap **Manage** in the drop-down. This takes you to the tag manager within Project Settings for all tags in your project.
{% figure %}

{% /figure %}
Tap on the **Create New Tag** in the upper right-hand corner of the tag manager tab to create a new tag. Enter a name and description for your new tag. Click on **Create**.
{% figure %}

{% /figure %}
After creating the tag, add it to any metric using the **+** icon when you hover on the metric. To see which metrics are associated with a given tag, refer to the **Tags** tab within Project Settings.
{% figure %}

{% /figure %}
After tagging your metrics, you can filter **Metric Lifts** in Pulse by the tagged collection to focus on the results that matter most.
{% figure %}

{% /figure %}
## Core tag
Every Statsig project includes an auto-created **Core** tag pre-populated with a subset of User Metrics. User Metrics are classic growth metrics (such as MAU, WAU, DAU, L7, and 28-day retention) that Statsig calculates daily from your logged events. The following User Metrics are added to the Core tag by default (you can remove them or replace them with more relevant business metrics):
* DAU/ WAU/ MAU
* New users
* Daily/ Weekly/ Monthly user stickiness
The Core tag serves as a collection of your most important business metrics to monitor with every new feature rollout and experiment.
The Core tag is automatically added to all new feature gates and experiments at the point of creation. In an experiment, the Core tag is added to the Scorecard, under **Secondary Metrics**. In a feature gate, the Core tag is added to the **Monitoring Metrics** section.
## Configuring your core metrics
Because the Core tag receives special treatment, curate the metrics in this tag collection during project setup. Go to **Metrics** → **Metrics Catalog** and add the **Core** tag to any relevant metrics.
To see which metrics already have the **Core** tag applied, filter by the **Core** tag in the Metrics Catalog by tapping the filter icon in the upper right-hand corner, then **Tags**, then selecting the **Core** tag. Remove the Core tag from any unwanted metrics in the filtered list.
{% figure %}

{% /figure %}
## Setting rollout alert thresholds for your core metrics
When rolling out a new feature or experiment, you may want to be notified if the rollout negatively impacts a key business metric. Statsig lets you set Rollout Alerts at the per-metric level. You receive an alert if any running experiment or feature gate regresses the metric beyond the set threshold.
Because Core Metrics are important, configure Rollout Alerts for Core Metrics through the Metrics Catalog. For more on Rollout Alert configuration, refer to [Rollout Alerts](/metrics/rollout-alerts).
================================================================================
# User Accounting Metrics (DAU/WAU/etc.)
URL: https://docs.statsig.com/metrics/user
Updated: 2026-07-11
================================================================================
## DAU (daily active user) definition
Statsig automatically creates a standard set of user accounting metrics to track common product-wide engagement metrics like daily active users (DAU), new users, and retention. Statsig also tracks more sophisticated metrics like L-ness, retention, and stickiness.
All Standard User Accounting Metrics rely on a company-wide definition of a daily active user. By default, Statsig defines a DAU as a [user that the Statsig SDK has logged](/sdks/user) any custom event (`log_event`) for. You can [customize the DAU definition](#customizing-the-dau-definition).
{% callout type="info" %}
**Warehouse Native Users**
You're viewing a feature designed for Statsig Cloud users. Warehouse Native customers typically have multiple datasets that uniquely affect how they define active users. Refer to
[Retention metrics in Warehouse Native](/statsig-warehouse-native/metrics/retention)
.
{% /callout %}
## Notation and conventions
* The first day a new user was active is Day Zero (D0), with subsequent days labeled D1, D2, D3, and so on.
* A weekly active user is someone active within the last 7 days (0-6 days), including users active on all 7 days and users active on only a single day. The same definition applies to a monthly active user.
* A user with a single session that spans midnight (with qualifying events at 11:59 PM and 12:01 AM) qualifies as a daily active user on both days.
* Statsig reserves the right to limit tracking to 100M unique IDs per unit type per year.
## Default user accounting metrics in Statsig
### General user metrics
| **Metric Name** | **Type** | **Description** |
|-----------------------------|--------------------|-------------------------------|
| `daily_active_user` | Count | Users who were active on a given calendar day (DAU). |
| `weekly_active_user` | Count | Users who were active at least once in the past 7 days (WAU). |
| `monthly_active_user` | Count | Users who were active at least once in the past 28 days (MAU). |
| `new_dau` | Count | Users who became active for the first time on a specific day. |
| `new_wau` | Count | Users who became active for the first time within the last 7 days. |
| `new_mau_28d` | Count | Users who became active for the first time within the last 28 days. |
| `daily_user_stickiness` | Stickiness (Rolling) | Fraction of the prior day's users who are active on the next day. Rolling day-to-day repeat engagement (**not** DAU/MAU or DAU/WAU). |
| `weekly_user_stickiness` | Stickiness (Rolling) | Fraction of the previous week's users who have been active within the last 7 days. This metric tracks rolling week-over-week repeat engagement (**not** WAU/MAU). The previous week is defined as 8-14 days before the metric date. |
| `monthly_user_stickiness` | Stickiness (Rolling) | Fraction of the previous month's users who have been active within the last 28 days. This metric tracks rolling month-over-month repeat engagement (**not** DAU/MAU). The previous month is defined as 29-56 days before the metric date. |
| `d1_retention_rate` | Retention (Rolling)| % of new users from 1 day ago who were active at least once today. Rolling Day 2 window retention. |
| `WAU @ D14 Retention Rate` | Retention (Rolling)| % of new users from 13 days ago who were active at least once in days 8–14. Rolling Week 2 window retention. |
| `MAU @ D56 Retention Rate` | Retention (Rolling) | % of new users from 56 days ago who were active at least once during days 29–56. Rolling Month 2 retention. |
| `L7` | L-ness | Average number of days a user was active in the last 7 days (value range: 0–7). |
| `L14` | L-ness | Average number of days a user was active in the last 14 days (value range: 0–14). |
| `L28` | L-ness | Average number of days a user was active in the last 28 days (value range: 0–28). |
These user metrics are useful for understanding long-term user behavior. However, several of these metrics don't behave well as daily experimentation metrics, because metrics like L7 are highly correlated across days. For example, a user with L7 = 7 on a given day will have either L7 = 6 or L7 = 7 the following day: this isn't a true daily independent variable. Such metrics are more likely to trigger false positive and false negative results. This limitation applies generally to stickiness and L-ness metrics.
## Customizing the DAU definition
You can customize the DAU definition in the Statsig Console by specifying or excluding a set of Statsig and custom events. Find this setting in *Project Settings*. You need the required privileges to edit it.
{% figure %}

{% /figure %}
By default, all events qualify a user as active. There are two ways to customize this:
1. **Excluding specific events:** Some companies exclude specific events that aren't significant user interactions (for example, `homepage_visit` or `notification_sent`). Expand the event list and uncheck events you don't want to include. You can also toggle whether future events (not shown in the list) should be included or excluded.
2. **Including specific events:** Some companies use a narrow definition of an active user (for example, event = `login`). Select the set of events and turn off **Include New Events by Default**.
Changes to the DAU definition take effect on the date of the change. Historical data remains unchanged. Statsig doesn't support backfill. Set your DAU definition before running any experiments or rolling out any features.
================================================================================
# Metric Directionality
URL: https://docs.statsig.com/metrics/directionality
Updated: 2026-07-11
================================================================================
## Setting metric directionality
By default, increases to a metric value are assumed to be good and appear in green in experiment results. For many metrics, a decrease is the wanted outcome: for example, page load times and most performance metrics.
For these metrics, you can set the direction you want the metric to move, and experiment results update color-coding in metric lifts accordingly. To configure this, go to any metric detail view page and select the **Edit Pencil** next to the *Directionality* section in the right rail to see the **Set Metric Directionality** option.
{% figure %}

{% /figure %}
================================================================================
# Metrics 301 - Real-time Analytics
URL: https://docs.statsig.com/metrics/301
Updated: 2026-07-11
================================================================================
This 301-level user guide shows you:
1. How to use [Events Explorer](/product-analytics/overview) to analyze sampled real-time events emitted by your application
2. How to create [Create User Funnels](/metrics/create-user-funnels)
3. How to create [Create User Flows](/metrics/create-user-flows)
Watch this space for more real-time and default product analytics features!
{% callout type="info" %}
**What's on the roadmap?**
Join the
[Statsig Slack channel](https://statsig.com/slack)
to ask for previews of Statsig's upcoming features or to submit a request. Statsig builds most of the roadmap based on requests from customers!
{% /callout %}
================================================================================
# Funnel Metrics
URL: https://docs.statsig.com/metrics/create-user-funnels
Updated: 2026-07-11
================================================================================
You can create a custom **funnel metric** from either the **Custom Metrics** creation wizard in the **Metrics Catalog** or through the **Charts** tab.
{% figure %}

{% /figure %}
## Components of Funnel Metrics
Funnel metrics have the following components:
1. **Lineage**: Surfaces the events used to generate the funnel
2. **Metric Value**: Metric value represents the overall funnel conversion rate, or the percentage of users who complete a funnel (trigger the end event) relative to all users who start the funnel (trigger the starting event)
3. **Roll-up Window**: Statsig calculates funnel metrics daily.
4. **Conversion Rate between Stages**: This set of metrics track the percentage of users who triggered an event N relative to all users that triggered event N-1 in the funnel
{% figure %}

{% /figure %}
After funnels are created and populated, you can view your funnel metric like any other metric in Pulse. You can also expand the funnel metric to view Pulse performance at each step in the funnel.
## User-based Funnel Metrics
When counting distinct events, funnel metrics sum the number of events per day over the analysis period. When counting distinct users, funnel metrics sum the number of distinct users per day over the analysis period.
For example, if a funnel consists of events A, B, and C in that order, user-based funnel metrics count the number of distinct users who triggered events A, B, and C on a given day. If you're tracking funnel conversion over multiple days, the daily granularity of funnel metrics may not suit your analysis.
## Example
In the example below, the **Square** variant shows a lift in the **overall funnel conversion rate**. Expanding the metrics to examine the entire funnel reveals two key insights:
* Both the **Square** and **Circle** variants show a lift in top-of-funnel DAU (*Land Page View Start DAU*). However, only the **Square** variant shows statistically significant increase in end-of-funnel DAU (*Purchase Event End DAU*).
* The overall funnel conversion rate improvement for **Square** is primarily due to the higher conversion from *Checkout Event* to *Purchase Event* stages in the funnel.
{% figure %}

{% /figure %}
================================================================================
# User Flows
URL: https://docs.statsig.com/metrics/create-user-flows
Updated: 2026-07-11
================================================================================
Statsig's **User Flows** enable you to visualize customer journeys through your application.
To create a **User Flow**,
1. Log into the Statsig console at https://console.statsig.com
2. On the left-hand navigation panel, select **Metrics** and then click on the **Charts** tab
3. Click on the **Create** button
4. Enter the chart name and start event of the User Flow that you want to visualize
5. Select the additional events that you want to include in the User Flow (Statsig will automatically determine the sequence of events)
6. Enter the number of days that users typically take to complete these events
7. You can also optionally enter the depth of the flow to define the scope of your analysis
8. Click on **Create** to finish
{% figure %}

{% /figure %}
If there is sufficient data for the User Flow, the chart appears in the console within a few minutes.
================================================================================
# Count Distinct Metrics
URL: https://docs.statsig.com/metrics/count-distinct
Updated: 2026-07-11
================================================================================
## Purpose of count\_distinct
The `count_distinct` metric reports the number of unique values at unit-level. At group-level, the mean is the sum of unit-level count distinct divided by the count of unique units exposed to the experiment. Because this metric relies on probabilistic HyperLogLog++ (HLL++) sketches, it carries a small estimation error (around 0.2%–0.3%), which may increase as the experiment window grows. By default, precision p = 12 is set for `count_distinct`, which provides good accuracy for most use cases while keeping results stable and resource usage efficient.
{% callout type="info" %}
Reach out to Statsig to enable sketch-based count distinct metrics for your project.
{% /callout %}
## For context, what are sketches?
Sketches are probabilistic summaries of large datasets that answer certain queries approximately, quickly, and using little memory (often a fixed-size or sublinear footprint, such as O(log n) or even constant space regardless of dataset size).
## Core principles of HLL++
1. **Hashing and Leading Zeros**
* Each input is hashed uniformly into a 64-bit integer.
* The position of the first 1-bit in the hash (the "rank") provides an estimate of how many distinct items precede that hash in sorted order.
2. **Register Array**
* The algorithm allocates *m* registers, where *m* = 2^p and *p* is the precision parameter.
* Each hashed value maps to one register. The register stores the maximum rank seen so far for values mapped to it.
3. **Sparse and Dense Modes**
* **Sparse mode** is used when the number of distinct items is below 2^(p+5). It keeps an exact list of non-zero registers in a compact form. This yields near-zero error for small cardinalities.
* **Dense mode** is used when the count exceeds the sparse limit. The sketch is represented as a fixed array of *m* registers, each one byte in size. Error increases slightly but remains tightly bounded.
4. **Estimation and Bias Correction**
* The raw estimate is computed as the harmonic mean of 2^(–register\_value) across all registers, multiplied by a constant factor (alpha\_m).
* BigQuery applies empirical bias corrections and the HIP (historic inverse probability) estimator to reduce variance and worst-case error.
## Precision, memory, and error relationship
The precision parameter *p* controls the number of registers *m* and thus the sketch's size and accuracy:
| Precision p | Registers m = 2^p | Memory per Sketch | Approximate Relative Standard Error (1 sigma) |
| --- | --- | --- | --- |
| 10 | 1,024 | ~1 KB | 0.83 / sqrt(1,024) ≈ 2.6 % |
| 12 | 4,096 | ~4 KB | 0.83 / sqrt(4,096) ≈ 1.3 % |
| 13 | 8,192 | ~8 KB | 0.83 / sqrt(8,192) ≈ 0.92 % |
| 15 | 32,768 | ~32 KB | 0.83 / sqrt(32,768) ≈ 0.46 % |
* Memory used grows linearly with *m*.
* Error decreases as the inverse square root of *m*.
* To halve the error, you must quadruple the number of registers.
**Examples of thresholds and steady‑state for common precisions:**
* **p = 10** (*m* = 1,024 registers, ~1 KB):
* Sparse limit: 2^(10+5) = 32,768 distincts (error ≈ 0%).
* Plateau begins at ~5·m = 5,120 distincts.
* Steady‑state error: 0.83/√1,024 ≈ 2.6%.
* **p = 12** (*m* = 4,096 registers, ~4 KB):
* Sparse limit: 2^(12+5) = 131,072 distincts.
* Plateau begins at ~5·m = 20,480 distincts.
* Steady‑state error: 0.83/√4,096 ≈ 1.3%.
* **p = 15** (*m* = 32,768 registers, ~32 KB):
* Sparse limit: 2^(15+5) = 1,048,576 distincts.
* Plateau begins at ~5·m = 163,840 distincts.
* Steady‑state error: 0.83/√32,768 ≈ 0.46%.
## Error behavior over cardinality
The relative error of an HLL++ sketch evolves through three phases:
* **Exact Counting Phase**
* Cardinality ≤ 2^(p+5). The sketch operates in sparse mode with error close to 0%.
* **Bias Ramp Phase**
* Cardinality between 2^(p+5) and approximately 5·m. The sketch is in dense mode. Systematic bias increases gradually from near 0 up to the maximum bound.
* **Steady-State Phase**
* Cardinality ≥ 5·m. Relative error stabilizes at the theoretical bound of 0.83 / sqrt(m). Once in this phase, adding more distinct items doesn't change the sketch's register distribution shape, so the error remains effectively constant. In other words, after enough uniques, the sketch "plateaus," and error fluctuations are limited to the estimator's minimal random variance around that fixed bound.
### Example for p = 15 (m = 32,768)
* **Exact in sparse mode (≤ 1,048,576 items)** - For up to 2^(p+5) = 2^20 = 1,048,576 distinct elements, HLL++ uses a compact "sparse" representation that records individual hashes directly, resulting in an exact count (0% error).
* **Transition to dense mode & rising error** - Beyond 1,048,576 distinct elements, it switches to the full register array of size m = 2^p = 32,768. In this *dense* regime, the Relative Standard Error (RSE) gradually increases from 0% to its theoretical limit.
* **Asymptotic error plateau (≈ 0.46%)** - Once in dense mode, the RSE quickly converges to RSE ≈ 0.83/√m = 0.83/√32,768 ≈ 0.46%, and remains at approximately 0.46% regardless of further increases in distinct count.
## Merging sketches over time or partitions
To compute distinct counts across multiple segments (for example, daily sketches), merge sketches by taking the element-wise maximum of their registers. This operation is associative and idempotent. Merged sketches preserve the same error bound as individual sketches and don't add additional error.
## Mathematical formulation
1. **Raw Estimate**
* *M\[i]* is the value of register *i*.
* *alpha\_m* is a bias correction constant dependent on *m*.
This is the core HyperLogLog "raw" estimator for the number of distinct elements in a multiset. You take each register value M\_i, compute $2^\{-M\_i}$, sum those values, invert that sum, and multiply by m^2 (where m is the number of registers) to get a harmonic-mean-based estimate. This is then scaled by the bias-correction constant α\_m. In effect, this formula transforms the observed distribution of leading-zero counts into an approximate count of unique items.
2. **HIP Estimator**
* Maintains a running sum of inverse probabilities for each new distinct element.
* Produces lower variance and a smaller worst-case error constant (0.83 instead of 1.04).
3. **Relative Standard Error (RSE)**
RSE ≈ 0.83/√m
## Best practices and limitations
* Choose precision *p* based on the maximum expected cardinality and your error tolerance. (Statsig sets this for you by default.)
* After Statsig creates a sketch with precision *p*, its precision can't be increased. Sketches can only be merged or downsampled to lower precision.
* Long keys or complex objects increase CPU and memory in sparse mode but don't affect dense mode size.
* Plan for a noise floor equal to the sketch's relative error when designing experiments or setting thresholds.
* Reach out if you believe you require higher precision.
## Summary and recommendations
HLL++ sketches provide efficient, bounded-error approximations for `count_distinct` queries, trading a small, predictable error for large savings in memory and compute.
* Memory scales as O(m) rather than O(n).
* Error scales as O(1/√m).
* Merging sketches is exact for unions, with no extra error.
Select the smallest precision *p* that meets your accuracy requirements to minimize storage and computation costs.
## Choosing precision for your cardinality
When you know the maximum number of unique items (cardinality) ahead of time, pick a precision *p* so that **5 · 2^p** exceeds that cardinality. This ensures the sketch is in its steady‑state phase, where the relative error remains at the bound of 0.83/√(2^p).
* **Up to 10,000 uniques**
* *p* = 12 (m = 4,096 registers) → memory ~4 KB
* Steady state from ~20,480 distincts (5·m) onward
* Relative error ≈ 0.83/√4,096 ≈ 1.3 %
* **Up to 50,000 uniques**
* *p* = 13 (m = 8,192 registers) → memory ~8 KB
* Steady state from ~40,960 distincts onward
* Relative error ≈ 0.83/√8,192 ≈ 0.92 %
* **Up to 100,000 uniques**
* *p* = 15 (m = 32,768 registers) → memory ~32 KB
* Steady state from ~163,840 distincts onward
* Relative error ≈ 0.83/√32,768 ≈ 0.46 %
* **Up to 500,000 uniques**
* *p* = 16 (m = 65,536 registers) → memory ~64 KB
* Steady state from ~327,680 distincts onward
* Relative error ≈ 0.83/√65,536 ≈ 0.32 %
* **Up to 1,000,000 uniques**
* *p* = 17 (m = 131,072 registers) → memory ~128 KB
* Steady state from ~655,360 distincts onward
* Relative error ≈ 0.83/√131,072 ≈ 0.23 %
* **General rule**
1. Compute *m* = 2^p
2. Ensure **5·m ≥ expected cardinality**
3. Verify memory footprint (*m* bytes) fits your budget
4. Confirm relative error (0.83/√m) meets your accuracy target
## How count_distinct fits into your workflow
`count_distinct` provides a consistent way to measure breadth at scale. Define a metric once, use it anywhere, and merge across time and partitions with stable behavior. The metric produces answers to “How many unique X?” without requiring custom ETL or one-off SQL.
================================================================================
# Parameter Stores
URL: https://docs.statsig.com/client/concepts/parameter-stores
Updated: 2026-07-11
================================================================================
Parameter Stores provide a way to organize and manage parameters in your web or mobile app through the Statsig console. Available for JS, React, React Native, Android, iOS, and Dart SDKs on the client side and Java, Node, Python, and Rust on the server side. Reach out in [Slack](https://statsig.com/slack) if you want support for a specific language.
## What is a Parameter Store?
Rather than thinking in terms of Statsig entities like Feature Gates, Experiments, Layers, or Dynamic Configs, Parameter Stores let you focus on **parameters**: the values in your app that need to be configurable remotely.
Parameter Stores **decouple your code from configuration**. This abstraction lets you run experiments, adjust gating, or change values dynamically **without hardcoding any experiment or gate names**. Instead, you define parameters that can be remapped remotely to any value or any Statsig entity.
## An Example: Parameterizing the Statsig Website
Release cycles are often more constrained on mobile platforms. As an example, the Statsig website marketing team frequently requests updates, so the team parameterizes text, images, buttons, colors, and more:
{% figure %}

{% /figure %}
To run an experiment, point these variables directly at experiments to start an A/B test without writing any code:
{% figure %}

{% /figure %}
The experiment on the tagline is now running without any code change. Access the parameter in code like this:
```javascript
const homepageStore = StatsigClient.getParameterStore("www_homepage");
const tagline = homepageStore.get("tagline", );
```
## How to Use Parameter Stores
Here’s a suggested workflow:
1. **Create a Parameter Store**:
Set up a Parameter Store for your team or project. Parameter Stores are designed to hold related parameters in one object.
2. **Identify Configuration Variables**:
Consider which variables you want to decouple from your app and control through Statsig rather than hardcoding them in your app. These could include:
* A boolean parameter to control access to a new feature. Even if you're used to using Feature Gates for boolean feature management, start with a boolean parameter instead.
* A string parameter for text resources that you may want to swap or experiment with.
* A number parameter for inputs such as the number of onboarding steps, a list length to truncate, and more.
3. **Start with Static Values**:
Begin with a static value for each parameter (what you would have hardcoded in the app). Use this static value initially.
4. **Remap When Ready**:
After your app is shipped and the feature is ready, remap the parameter to a Feature Gate, Experiment, Dynamic Config, or Layer to test and target different variants.
5. **Update in Real-Time**:
After experimenting, you can update the static value or gate the feature for specific app versions. This can be done in real time across mobile apps that are already released.
## Why Use Parameter Stores?
Parameter Stores are inspired by solutions such as Facebook’s Mobile Config, Uber’s experimentation approach, and Firebase Remote Config. Leading mobile companies use these patterns to:
* Move faster
* Maintain backward compatibility
* Experiment more freely
The extra setup in the Statsig console takes more initial effort than creating a gate, but saves time after the app is shipped. Parameter Stores ensure that no Statsig entity values are hardcoded, giving you flexibility to update parameters without a new release. This is particularly useful for mobile apps, where app store release cycles create delays unlike backends or websites.
## Supported SDK Versions
Parameter Stores are available in the following SDKs:
* **Android SDK** v4.33.0+
* **iOS SDK** v1.45.0+
* **@statsig/js-client**, **@statsig/react-bindings**, **@statsig/react-native-bindings**, **@statsig/expo-bindings** v1.4.0+
* **Dart SDK** v1.2.1+
* **Statsig Server Core SDKs** are gradually adding support, available in:
* **com.statsig:javacore** 0.1.0+
* **statsig-rust** 0.1.0+
* **@statsig/statsig-node-core** 0.1.0+
* **statsig-python-core** 0.5.0+
* **statsig-dotnet-core** 0.6.1+
Parameter Stores aren't available in non-server-core server SDKs, and aren't available when bootstrapping a client SDK from one of those SDKs. For support in a specific language, reach out in [Slack](https://statsig.com/slack).
***
================================================================================
# Build your first Device-level Experiment
URL: https://docs.statsig.com/guides/first-device-level-experiment
Updated: 2026-07-11
================================================================================
When you can't identify a user through a user ID, device-level experiments let you randomize experiments based on a consistent identifier for the user's device. Statsig can automatically generate a stable ID, but Statsig recommends using your own cookie or logged-out ID when possible.
Device-level experiments are ideal in scenarios such as:
* **Anonymous or first-time users**: When users haven't signed in yet or are browsing anonymously.
* **Cross-device consistency**: Ensuring the same experience on the same device, regardless of user sign-in status.
You can implement a device-level experiment almost exactly like a traditional user-level experiment. The key difference is setting the experiment’s ID type. This example uses `stableID` as the ID type, but you can substitute your own identifier if you have one.
## Step 1: Create a Device-level Experiment
1. **Log into the Statsig Console**: Visit [Statsig Console](https://console.statsig.com/) and navigate to **Experiments+** on the left-hand sidebar.
2. **Create a New Experiment**:
* Click on **Create** and fill out the **name** and **description** of your experiment.
* Enable the **Use Stable ID** option during setup.
* Click **Create** to proceed.
{% figure %}

{% /figure %}
3. **Define Experiment Metrics**: Add a hypothesis, primary metrics, and secondary metrics in the **Scorecard** section, the same as for a user-level experiment.
4. **Set Groups and Parameters**:
* In the **Groups** section, define the parameters for your experiment. For instance, you can experiment with a simple boolean parameter like `"enabled"`.
{% figure %}

{% /figure %}
5. **Set Allocation**:
* By default, the experiment targets 100% of your user base. Adjust the allocation if needed. Start with a smaller rollout until you're confident in the new variant.
{% figure %}

{% /figure %}
6. **Save and Start**: Once you've configured everything, click **Save** to finalize your experiment. When you're ready to launch, click **Start** to roll it out.
## Step 2: Initialize the SDK in your application
After setting up your experiment in the Statsig console, integrate it into your client application using one of Statsig’s SDKs.
**Important:**
* Set **`userID`** only for authenticated, logged-in users.
* For logged-out or anonymous users, use **`stableID`** (Statsig’s auto-generated device ID) or your own custom deviceID to identify the device. Refer to [customID types](/guides/experiment-on-custom-id-types) if you have your own deviceID.
* Always pass all known IDs to the SDK. Statsig uses the correct one for evaluation based on the experiment or gate’s ID type.
* If you do rely on stableID, only Statsig client SDKs (javascript, react, mobile, etc) generate it. Server SDKs can't generate this ID for you.
* **User attributes**: You can pass additional attributes like `appVersion`, and `custom` properties for experiment targeting.
### Example (JavaScript):
```javascript
const user = {
userID: userID: isLoggedIn() ? getLoggedInUserID() : undefined,
// Optional attributes to help with targeting
appVersion: "1.0.0",
custom: {
promoCode: "New30Off"
}
};
// Initialize the Statsig Client
const client = new StatsigClient(sdkKey, user, {
environment: { tier: "production" }
});
await client.initializeAsync();
```
{% callout type="info" %}
If your app collects other relevant attributes (e.g., device type, region), pass them in the `user` object to improve experiment precision.
{% /callout %}
## Step 3 (Optional): Update User Info for Logged-in Users
When a user signs in or creates an account, call the SDK’s updateUser method to attach userID and other logged-in attributes. This enables user-level experiments and gates to evaluate with the authenticated identifier.
### Example (JavaScript):
```javascript
const updatedUser = {
...user, // continue to send the deviceID!
userID: loggedInID,
email: signUpEmail
};
// Update the user object
await client.updateUserAsync(user);
```
{% callout type="note" %}
Adding userID after login enables user-level experiments/gates to target and evaluate using userID. *Device-level* experiment evaluations remain based on stableID (or deviceID) and don't change when you add userID, as long as you continue to pass that identifier as well! This preserves consistency of device-level bucketing.
{% /callout %}
***
================================================================================
# Experiment on custom Unit ID types
URL: https://docs.statsig.com/guides/experiment-on-custom-id-types
Updated: 2026-07-11
================================================================================
In some cases, you may want to randomize experiment bucketing using a custom Unit ID instead of the default `userID` or Statsig-generated Stable ID. For example, if you run a task management tool for companies and want to experiment on company-wide behaviors, you might use `companyID` as the Unit ID. Using `companyID` ensures all users from the same company get the same experience, so you can measure overall productivity impacts at the company level.
Custom ID types allow for this flexibility in your experiments and feature gates, enabling you to randomize and control rollouts based on any identifier you need.
### When to use custom Unit IDs
* **Organization-wide experiments**: Group users by `companyID` for company-wide consistency.
* **Session-based experiments**: Use a session or device ID to control the experiment within a specific session.
* **Group-level rollouts**: Target teams, regions, or other specific cohorts using a relevant ID type.
Follow these three steps to set up an experiment with a custom Unit ID. The following examples use `companyID`, but you can replace it with any ID relevant to your use case.
{% callout type="note" %}
These steps also apply to feature gates, allowing you to partially roll out features based on custom Unit IDs.
{% /callout %}
***
### Step 1: Add `companyID` as a Custom Unit ID
1. **Log into the Statsig Console**: Head over to [Statsig Console](https://console.statsig.com/) and navigate to **Project Settings**.
2. **Find Custom Unit IDs**: Under **Manage Account** > **Info**, look for the **Custom Unit IDs** section.
{% figure %}

{% /figure %}
3. **Add a New Custom Unit ID**:
* Click the **Edit** button.
* Enter `companyID` as the new ID type and provide a description.
* Save the changes.
{% figure %}

{% /figure %}
After adding a custom Unit ID, you can use it across all Statsig configurations: experiments, gates, layers, dynamic configs, and autotunes. You only need to configure it once per project. Statsig then makes the custom Unit ID available for targeting and analysis across experiments, feature flags, and dynamic configs.
### Step 2: Select `companyID` as the ID Type in Your Experiment
1. **Create a New Experiment**:
* Navigate to **Experiments+** in the Statsig Console.
* When setting up a new experiment, find the **ID Type** dropdown and select `companyID`.
{% figure %}

{% /figure %}
2. **Complete Experiment Setup**:
* Finish configuring your experiment as you would for any [user-level experiment](/guides/abn-tests).
* Define your hypothesis, metrics, and target audience.
After completing the setup, click **Save**.
Choosing the right custom Unit ID ensures Statsig places all users with the same `companyID` in the same experiment group, making it easier to measure performance at the group level.
***
### Step 3: Provide `companyID` in the Statsig SDK
To use the custom Unit ID in your application, provide it when initializing the Statsig SDK. The `customIDs` field in the Statsig user object lets you pass the `companyID` (or any other custom ID) along with the usual user information.
#### Example (JavaScript):
```javascript
var user = {
userID: "some_user_id", // Standard user identifier
customIDs: {
companyID: "some_company_id" // Custom ID for grouping
},
// Other attributes (optional)
email: "user@example.com",
appVersion: "1.0.0"
};
// Initialize the Statsig Client
const client = new StatsigClient(sdkKey, user);
await client.initializeAsync();
```
* **`customIDs` field**: This allows you to pass a dictionary of custom IDs, including `companyID`, to ensure the experiment targets users based on that ID.
After providing the necessary IDs in the SDK, you can start logging events and fetching experiment configurations based on the custom Unit ID.
***
================================================================================
# Environment-based Evaluation
URL: https://docs.statsig.com/guides/using-environments
Updated: 2026-07-11
================================================================================
Statsig SDKs let you set the environment tier for your app during initialization. This lets you evaluate feature gates, dynamic configs, and experiments differently in non-production environments like development or staging. Configure the appropriate environment in your code and adjust feature rules in the Statsig Console.
***
## Configure environments in the SDK
There are two key ways to set up environments within your app:
1. **Environment-specific SDK keys**: These determine which rule sets are downloaded by the SDK based on the environment.
2. **Environment tier at SDK initialization**: This defines how rules are evaluated for the app.
### 1. Environment-specific SDK keys
Environment-specific SDK keys control which rules Statsig sends to the SDK. For example, an SDK initialized with a development environment key doesn't receive rules set for staging or production environments. For more information, go to [Per-Environment API Keys](#per-environment-api-keys) below.
### 2. Environment tier parameter
SDK keys can correspond to multiple environments. Explicitly set the environment tier during SDK initialization to ensure Statsig applies the correct rules.
All SDKs accept an `SDK Key` and an optional `StatsigOptions` dictionary. The `StatsigOptions` parameter includes the `environment` key, which has a `tier` field. This tier corresponds to one of your pre-configured environments (e.g., development, staging).
{% callout type="note" %}
If the environment tier is unset, all checks and event logs default to "production."
{% /callout %}
Here's an example of setting the environment tier in your code for the **development** environment:
#### Example (JS Client SDK):
```javascript
const client = new StatsigClient(, user, { environment: { tier: 'development' } });
```
#### Example (Node Server SDK):
```javascript
await statsig.initialize(, { environment: { tier: 'development' } });
```
Go to your language-specific SDK documentation for details.
***
## Using environments in feature gates
To configure environment-specific rules for a feature gate, follow these steps:
1. **Create a new feature gate**: In the Statsig Console, create a new Feature Gate. For example, name it "development mode" to target only your development environment.
{% figure %}

{% /figure %}
2. **Specify environments**: When configuring the rule, check the **Specify Environments** box and select the environments you want to target. By default, rules are enabled for all environments unless specified otherwise.
{% figure %}

{% /figure %}
3. **Save your settings**: After saving, the environments where the rule is enabled appear below the rule name.
{% figure %}

{% /figure %}
You can also filter rules by environment using the filter in the upper-right corner of the Feature Gate UI.
To edit the target environments of a rule, click **...** next to the rule name and select **Edit Rule**.
{% figure %}

{% /figure %}
***
## Configuring environments
By default, Statsig provides three environments: **Development**, **Staging**, and **Production**. You can add more environments or rename the default ones, but the **Production** environment can't be deleted or modified.
### Steps to add or edit environments
1. Go to **Project Settings** → [**Environments & Keys**](https://console.statsig.com/api_keys).
{% figure %}

{% /figure %}
2. Click **Edit** to add new environments or reorder the existing ones using drag-and-drop.
{% figure %}

{% /figure %}
{% callout type="note" %}
Reordering environments doesn't affect any rule logic, but it helps convey the rollout hierarchy (e.g., development → staging → production) to your teams.
{% /callout %}
***
## Per-environment API keys
Statsig lets you create per-environment API keys to enhance security and privacy. SDKs initialized with a specific environment key only access the rules relevant to that environment.
### Steps to generate environment-specific API keys
1. Go to **Project Settings** → [**Environments & Keys**](https://console.statsig.com/api_keys).
2. Click **Generate New Key** and specify the environment for which you want to generate the API key.
{% figure %}

{% /figure %}
{% callout type="note" %}
The default environments (Development, Staging, and Production) share the same server and client-side API keys. You can generate new keys for custom environments as needed.
{% /callout %}
***
================================================================================
# Ingesting Open Telemetry Data
URL: https://docs.statsig.com/server/concepts/open_telemetry
Updated: 2026-07-11
================================================================================
{% callout type="tip" %}
Setting up OTEL can be complex. If you have questions, contact us on Slack.
{% /callout %}
## How OpenTelemetry integration works
This guide walks you through setting up the OpenTelemetry Collector in your Kubernetes environment and configuring it to send telemetry data to Statsig using the official `otlphttp` exporter.
***
## ✅ Prerequisites
* A running Kubernetes cluster (e.g., GKE, EKS, Minikube, etc.)
* Helm installed, or another mechanism to apply helm charts to your cluster, like ArgoCD
* API Access to a Statsig project with a Server SDK Secret Key
* Optional: `kubectl` configured and connected to the cluster, for validating and debugging setup
***
## 1. Setup OpenTelemetry Collector on Kubernetes
The recommended setup follows the official OpenTelemetry Kubernetes guide to install the OpenTelemetry Collector in both **DaemonSet** and **Deployment** modes using the official Helm chart. Tested `values.yaml` files for both deployment modes are available in Step 3.
**📘 Official Guide:**
[OpenTelemetry Collector for Kubernetes – Getting Started](https://opentelemetry.io/docs/platforms/kubernetes/getting-started/)
### Why both?
* **DaemonSet Collector**: Runs one instance per node to collect host-level telemetry (e.g., logs, pod and node metrics).
* **Deployment Collector**: Runs one instance per cluster to gather telemetry related to the cluster as a whole.
Configure at least the receivers below for a useful Statsig-powered observability platform for your Kubernetes workloads. The setup uses [Presets](https://opentelemetry.io/docs/platforms/kubernetes/helm/collector/#presets) from the official chart and customizes some components, as shown in the sample values files in Step 3.
* Minimum for scraping logs:
* [Filelog Receiver](https://opentelemetry.io/docs/platforms/kubernetes/getting-started/#filelog-receiver)
* [Kubernetes Attributes Processor](https://opentelemetry.io/docs/platforms/kubernetes/getting-started/#kubernetes-attributes-processor)
* Minimum for scraping kubernetes metrics
* [KubeletStats Receiver](https://opentelemetry.io/docs/platforms/kubernetes/getting-started/#kubeletstats-receiver)
* [Kubernetes Cluster Receiver](https://opentelemetry.io/docs/platforms/kubernetes/getting-started/#kubernetes-cluster-receiver)
* Minimum for metrics about Otel Collector itself
* [OTLP Receiver](https://opentelemetry.io/docs/platforms/kubernetes/getting-started/#otlp-receiver) for [scraping internal metrics](https://opentelemetry.io/docs/collector/internal-telemetry/#configure-internal-metrics) (alternatively scraped through prometheus receiver)
***
## 2. Configure exporting telemetry to Statsig with OTLP HTTP Exporter
Statsig supports receiving OTLP-formatted, JSON-encoded telemetry at the endpoint `https://api.statsig.com/otlp`. The OpenTelemetry Collector supports sending telemetry to the Statsig endpoint using the official `otlphttp` exporter.
### Authentication
Requests are authenticated using the request header `statsig-api-key` and a valid SDK Server Secret key generated from [console.statsig.com](http://console.statsig.com) (under *Settings > Keys & Environments*). Keep your secret key secure using a Kubernetes Secret management provider and make it available to the OpenTelemetry collector pod’s environment.
### Example Exporter Config
```yaml
exporters:
otlphttp:
endpoint: https://api.statsig.com/otlp
encoding: json
headers:
statsig-api-key: ${env:STATSIG_SERVER_SDK_SECRET}
```
## 3. Example Helm Chart Values for a quick and correct setup
The following two complete and tested `values.yaml` configurations work with the OpenTelemetry Helm charts:
### 🔗 Deployment Collector
👉 [values-gateway.yaml](https://gist.github.com/karan-statsig/d5c70b7fd100ab445985b620dcb7e8c6)
### 🔗 DaemonSet Collector
👉 [values-agent.yaml](https://gist.github.com/karan-statsig/f02ee95fef00aecde402829b5a4c60e8)
Use them with the Helm chart version `0.75.1` like this:
```yaml
helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update
# Install Deployment Collector
helm install otel-deployment open-telemetry/opentelemetry-collector \
--version 0.75.1 \
-f values-gateway.yaml -n otel --create-namespace
# Install DaemonSet Collector
helm install otel-daemonset open-telemetry/opentelemetry-collector \
--version 0.75.1 \
-f values-agent.yaml -n otel
```
## 4. Verify the Setup
Check that all pods are running:
```bash
kubectl get pods -n otel
```
Check logs for a specific collector pod and confirm no errors are reported:
```bash
kubectl logs -n otel $pod_name
```
## 5. Explore
Explore your logs and metrics at [console.statsig.com](http://console.statsig.com) using the Logs Explorer and Metrics Explorer products under *Analytics* in the sidebar.
***
## 🔗 Resources
* [OpenTelemetry Collector Documentation](https://opentelemetry.io/docs/collector/)
* [Important Concepts for Kubernetes](https://opentelemetry.io/docs/platforms/kubernetes/collector/components/)
* [Helm Chart Reference](https://github.com/open-telemetry/opentelemetry-helm-charts)
* [Collector Configuration Reference](https://opentelemetry.io/docs/collector/configuration)
* [OTLP Protocol Specification](https://opentelemetry.io/docs/specs/otlp/)
================================================================================
# Testing your Gates/Experiments
URL: https://docs.statsig.com/guides/testing
Updated: 2026-07-11
================================================================================
Statsig provides tools that let you test configurations quickly while ensuring reliable outcomes. This page highlights key features to help you test efficiently.
***
## Overrides: test features, experiments, or holdouts
Overrides allow you to manually configure [features](/feature-flags/overrides#adding-an-override), [experiments](/experiments/setup/overrides), or [holdouts](/experiments/holdouts-introduction) for testing purposes. This method enables safe testing without affecting live production data or skewing experiment results. Statsig excludes overrides from Pulse analysis to maintain unbiased results.
* Use **Segments** to target overrides to pre-production environments or specific groups (e.g., employees) for testing.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
For more details on adding overrides, go to:
* [Feature Overrides](/feature-flags/overrides#adding-an-override)
* [Experiment Overrides](/experiments/setup/overrides)
* [Holdout Overrides](/experiments/holdouts-introduction)
***
## Unit testing with Statsig
Statsig's server SDKs offer a `localMode` feature that disables network access, ensuring that tests run locally and independently of production systems. When `localMode` is active, the SDK returns default values, allowing you to mock features and experiments in a controlled test environment.
### Override APIs for testing
You can use the `overrideGate` and `overrideConfig` APIs to set specific overrides for users or globally during testing.
```js
function overrideGate(
gateName: string,
value: boolean,
userID?: string,
): void;
```
```js
function overrideConfig(
configName: string,
value: object,
userID?: string,
): void;
```
For example, to override a gate for testing:
```js
statsig.overrideGate("example_gate", true);
```
* For more information on mocking Statsig for testing, go to [Node.js Server SDK](/server/nodejsServerSDK#how-can-i-mock-statsig-for-testing) or [JavaScript Client SDK](/client/javascript-sdk#testing).
***
## Environments: configuring Development, Staging, and Production
Statsig lets you assign environments to feature gates, experiments, and events. By default, Statsig assigns checks without a defined environment to **Production**. You can customize environments (such as **Development**, **Staging**, or **Production**) and use them to target different versions of feature gates or segments.
* Non-production events appear in diagnostics and Statsig uses them to track cumulative exposures and metric results when testing experiments in lower environments with **Enable for Environments**. Production data is prioritized for final Pulse result analyses.
### Customizing environments
You can map your internal environments to Statsig's built-in environments or create custom mappings. Common setups include:
* Assigning Dev One boxes to **Development**.
* Creating an **Early Access** slice (e.g., 1% of production users) as part of the **Production** environment for phased rollouts.
For more details on environments, go to the [Statsig blog post](https://blog.statsig.com/environments-on-statsig-6a818805b3c2).
***
================================================================================
# Migration Overview
URL: https://docs.statsig.com/guides/migration-overview
Updated: 2026-07-11
================================================================================
Statsig combines feature flags, experimentation, and product analytics in one platform. Migrating ensures your data, flag, experiments, and decision workflows live in a single source of truth.
This guide outlines the overall migration process. For provider-specific steps (Amplitude, LaunchDarkly, etc.), refer to the dedicated guides.
## Migration phases
### 1. Audit and plan
* Identify the datasets, events, and feature flags you want to move
* Decide what needs full historical backfill vs. what can start fresh
* Document any dashboards or KPIs that need rebuilding in Statsig
### 2. Set up live data
* Implement Statsig SDKs to start streaming new events and feature flag evaluations
* Validate critical events are firing with the correct schema
* Use Statsig for the newly recorded events and flags
### 3. Import historical data
* Export data from your existing tool (S3 or warehouse is preferred)
* Transform the schema to Statsig's event format (`event`, `user`, `timestamp`, `metadata`)
* Import using Statsig's Event Webhook, S3 ingestion, or warehouse ingestion
### 4. Validate and decommission
* Compare metrics between your legacy tool and Statsig to ensure parity
* Rebuild dashboards and charts in Statsig
* Decommission old pipelines once Statsig is your single source of truth
## Best practices
* **Start small**: Run a pilot project or test migration before backfilling all history
* **Align IDs early**: Ensure `userID` and `stableID` mapping is consistent. Identity mismatches are the most common failure point
* **Shard historical imports**: Break large datasets into daily partitions for stability
* **Rebuild insights intentionally**: Don't port all events and flags directly. Use migration as a chance to clean up stale data
* **Plan change management**: Teams need time to adjust workflows, queries, and dashboards so migrate for 1-2 teams before championing in the broader org
## Provider-specific guides
* [Migrate from Amplitude](/guides/migrate-from-amplitude)
* [Migrate from Mixpanel](/guides/migrate-from-mixpanel)
* [Migrate from LaunchDarkly (Feature Flags)](/guides/migrate-from-launchdarkly)
* Additional guides coming soon
## Get help
Statsig has helped many customers migrate from other tools. For tailored guidance, reach out to the support team, your sales contact, or through the [Slack community](https://statsig.com/slack).
================================================================================
# Migrate your analytics data from Amplitude
URL: https://docs.statsig.com/guides/migrate-from-amplitude
Updated: 2026-07-11
================================================================================
Statsig is an all-in-one platform that combines analytics, experimentation, and feature flagging. Migrating Amplitude data into Statsig usually involves two steps: export and ingest. This guide provides the essentials. For anything beyond these basics, contact Statsig.
## Step 1. Export your data from Amplitude
Amplitude offers a few different export methods. Pick the one that matches your data size and setup:
**1. S3 Export**
For high volume backfills, dump your Amplitude data into an S3 bucket.
**2. Warehouse Export**
If your Amplitude data is already in Snowflake, BigQuery, or Redshift, you can skip file downloads. Statsig ingests directly from these warehouses (refer to Step 2.1).
**3. Export API**
Use Amplitude's Export API to pull gzipped JSON
* **Limit**: 4 GB per request so use hourly windows for large ranges
* **Example**:
```bash
curl --location --request GET 'https://amplitude.com/api/2/export?start=&end=' \
-u '{api_key}:{secret_key}'
```
**4. UI Download (CSV/JSON)**
Go to Organization Settings → Project → Export Data
* Best for small datasets or initial testing
## Step 2. Transform your data
Amplitude and Statsig store events in slightly different formats. Map your Amplitude data to Statsig's format before importing, regardless of which import method you use in Step 3.
| Amplitude field | Statsig field |
|-----------------|---------------|
| `event_type` | `event` |
| `event_time` | `timestamp` (ms since epoch) |
| `user_id` | `user.userID` |
| `device_id` | `user.stableID` |
| `event_properties` | `metadata` |
| `user_properties` | `user` fields |
**Before transform**
```json
// Amplitude event
{
"event_type": "purchase",
"user_id": "123",
"device_id": "device_abc",
"event_time": "2023-08-17T00:00:00Z",
"event_properties": {
"amount": 25,
"currency": "USD"
},
"user_properties": {
"plan": "premium"
}
}
```
**After transform**
```json
// Statsig event
{
"event": "purchase",
"user": {
"userID": "123",
"stableID": "device_abc",
"plan": "premium"
},
"timestamp": 1692230400000,
"metadata": {
"amount": 25,
"currency": "USD"
}
}
```
## Step 3. Import into Statsig
After your data is in Statsig event format, choose an import path based on how you exported:
| If you exported from Amplitude via... | Import into Statsig using... | Best when... |
|---------------------------------------|------------------------------|--------------|
| S3 export | S3 ingestion | You're backfilling large datasets |
| Warehouse (Snowflake/BQ/Redshift) | Warehouse ingestion | Your Amplitude data already lives in a warehouse |
| Export API | Event Webhook | You're moving a few days/weeks of data programmatically |
| UI download (CSV/JSON) | Event Webhook | You're testing or moving a small slice of data |
**S3 ingestion**
* Ensure files are transformed to the Statsig schema in Parquet, JSON, or CSV format, then follow the [Statsig S3 ingestion](/data-warehouse-ingestion/s3) steps.
* Shard your Amplitude raw data into one day per directory before ingesting into Statsig.
**Warehouse ingestion**
* [Connect your warehouse to Statsig](/data-warehouse-ingestion/introduction)
* Point Statsig at a query that outputs events in the expected schema
* Statsig ingests on a recurring schedule
**UI download or Export API**
To get these events into Statsig, replay them through the [Event Webhook](/http-api/overview#post-event-webhook). Take each row or JSON object, reshape it to Statsig format, and send it one at a time or in small batches.
This approach is best for test runs or initial migrations, not for millions of events.
```bash
curl -X POST https://api.statsig.com/v1/webhooks/event_webhook \
-H "Content-Type: application/json" \
-H "STATSIG-API-KEY: $STATSIG_SERVER_SECRET" \
-d '{
"event": "signup",
"user": { "userID": "abc" },
"timestamp": 1692230400000
}'
```
## Get migration help
If you are unsure how to approach this migration, contact the Statsig team. Statsig has worked closely with customers migrating from Amplitude and can provide hands-on support.
Reach out to the support team, your sales contact, or through the [Slack community](https://statsig.com/slack).
================================================================================
# Migrate your analytics data from Mixpanel
URL: https://docs.statsig.com/guides/migrate-from-mixpanel
Updated: 2026-07-11
================================================================================
Migrating from Mixpanel to Statsig gives teams a unified platform that combines analytics, experimentation, and feature flagging, which enables faster and more informed decisions without data silos.
Migrating Mixpanel data into Statsig usually involves three steps: export, transform, and ingest. This guide provides the essentials. For anything beyond these basics, contact Statsig.
{% callout type="note" %}
This guide covers only importing raw events into Statsig. Statsig doesn't support importing user/group profiles, dashboards, reports, etc.
Once your raw events are in the Statsig project, you can re-create your critical dashboards there.
{% /callout %}
## Step 1. Export your data from Mixpanel
Mixpanel offers a few different export methods. Pick the one that matches your data size and setup:
**1. CSV Export**
Export small batches of events as CSV using the Events tab → query events → click "Export" button.
**2. Export API**
Use Mixpanel's [Raw Event Export API](https://developer.mixpanel.com/reference/raw-event-export) to pull JSONL data:
* **Limit**: Export one day's data at a time for optimal performance
* **Format**: JSONL where each line is a valid JSON object
```bash
curl --location --request GET 'https://data.mixpanel.com/api/2.0/export?from_date=2023-01-01&to_date=2023-01-01' \
-u '{project_id}:{service_account_secret}'
```
**3. Data Pipelines (Bulk Export)**
For large data volumes, use Mixpanel's [Data Pipelines](https://docs.mixpanel.com/docs/data-pipelines) feature to export to:
* Cloud Storage (AWS S3, Google Cloud Storage, Azure Blob Storage)
* Data Warehouse (BigQuery, Redshift, Snowflake)
## Step 2. Transform your data
Mixpanel and Statsig store events in slightly different formats. Map your Mixpanel data to Statsig's format:
| Mixpanel field | Statsig field |
|----------------|---------------|
| `event` | `event` |
| `properties.time` | `timestamp` (ms since epoch) |
| `properties.distinct_id` or `properties.user_id` | `user.userID` |
| `properties.device_id` | `user.stableID` |
| `properties.*` (other fields) | `metadata` |
**Before transform**
```json
// Mixpanel event
{
"event": "Signed up",
"properties": {
"time": 1618716477,
"distinct_id": "user-123",
"device_id": "xyz",
"Referred_by": "Friend",
"URL": "website.com/signup"
}
}
```
**After transform**
```json
// Statsig event
{
"event": "Signed up",
"user": {
"userID": "user-123",
"stableID": "xyz"
},
"timestamp": 1618716477000,
"metadata": {
"Referred_by": "Friend",
"URL": "website.com/signup"
}
}
```
## Step 3. Import into Statsig
After your data is in Statsig event format, choose an import path:
| If you exported from Mixpanel via... | Import into Statsig using... | Best when... |
|--------------------------------------|------------------------------|--------------|
| S3 export | [S3 ingestion](/data-warehouse-ingestion/s3) | You're backfilling large datasets |
| Warehouse (Snowflake/BigQuery/Redshift) | [Warehouse ingestion](/data-warehouse-ingestion/introduction) | Your Mixpanel data already lives in a warehouse |
| Export API | [Event Webhook](/http-api/overview) | You're moving a few days/weeks of data programmatically |
| CSV download | [Event Webhook](/http-api/overview) | You're testing or moving a small slice of data |
**Event Webhook (for API/CSV exports)**
```bash
curl -X POST https://api.statsig.com/v1/webhooks/event_webhook \
-H "Content-Type: application/json" \
-H "STATSIG-API-KEY: $STATSIG_SERVER_SECRET" \
-d '{
"event": "Signed up",
"user": {
"userID": "user-123",
"stableID": "xyz"
},
"timestamp": 1618716477000,
"metadata": {
"Referred_by": "Friend",
"URL": "website.com/signup"
}
}'
```
**Important notes:**
* **S3 ingestion**: Shard your Mixpanel data into 1 day's data per directory for Statsig
* **Scale gradually**: After small tests, backfill in chunks to manage loads
* **Future tracking**: After historical import, switch Mixpanel code calls to Statsig SDKs
## Get migration help
If you are unsure how to approach this migration, contact the Statsig team. Statsig has experience helping customers migrate from Mixpanel.
Reach out to the support team, your sales contact, or through the [Slack community](https://statsig.com/slack).
================================================================================
# LaunchDarkly Migration Guide
URL: https://docs.statsig.com/guides/migrate-from-launchdarkly
Updated: 2026-07-11
================================================================================
## How this guide is organized
This guide covers the following topics:
1. Conceptual differences between LaunchDarkly and Statsig
2. Deciding what to migrate vs. not
3. Importing flags into Statsig
4. Flipping evaluation from LaunchDarkly to Statsig
5. How to run the migration process
## Conceptual differences between LaunchDarkly and Statsig
LaunchDarkly and Statsig structure their feature management data models differently in several key ways:
**Environment**: LaunchDarkly treats environments as a top-level concept where flags and segments must be duplicated and managed separately across environments. Statsig uses a centralized model where flags and configs handle environment-specific logic in their targeting rules.
**Flag types**: LaunchDarkly uses a mix of boolean, multivariate, and JSON flags. Statsig distinguishes between Feature Gates (boolean) and Dynamic Configs (typed multivariate configs with JSON values).
**Targeting**: LaunchDarkly relies on Contexts to evaluate flags. Statsig evaluates based on a StatsigUser object.
#### Side by side comparison
| LaunchDarkly concept | Can we migrate? | Statsig notes |
|---------------------|-----------------|---------------|
| Project | ✅ Yes | Convert to Project |
| Environment | ✅ Yes | Convert to Environment (mark critical as production in Statsig) |
| Boolean Flags | ✅ Yes | Convert to Feature Gates |
| String, Number, and JSON Flags | ✅ Yes | Convert to Dynamic Configs |
| Segments | ✅ Yes | Convert to Segments (Big ID list segments won't be imported) |
| Targeting Rules | ✅ Yes | Convert to Rules |
| Context kind | ✅ Yes | Convert to Custom Unit ID in Statsig |
| Context attribute | ✅ Yes | Convert to Custom Fields in Statsig |
| Flag owner, tags, teams, and history | ❌ No | Statsig doesn't preserve any metadata or historical versions of a flag during migration |
#### User Context mapping example
LaunchDarkly supports multi-kind, structured user contexts. Statsig requires a user object to achieve this. In Statsig, User ID or Custom ID is equivalent to LD's key. Known top-level fields in Statsig include userID, email, ip, userAgent, and custom. All other fields go under the custom object.
**Example 1: LD User context to Statsig User object conversion**
```javascript
// 1 - LD User context
{
"kind": "user",
"key": "user-key-123abc",
"name": "Anna",
"email": "anna@globalhealthexample.com",
"organization": "Global Health Services",
"jobFunction": "doctor",
"device": "iPad"
}
// 1 - Statsig User object
{
"userID": "user-key-123abc",
"email": "anna@globalhealthexample.com",
"custom": {
"name": "Anna",
"organization": "Global Health Services",
"jobFunction": "doctor",
"device": "iPad"
}
}
```
**Example 2: LD Multi context kind to Statsig User object conversion**
```javascript
// 2 - LD Multi context kind
{
"kind": "multi",
"user": {
"key": "user_abc",
"name": "Anna",
"email": "abc@company.com",
"region": "us-east"
},
"org": {
"key": "org_xyz",
"tier": "enterprise"
}
}
// 2 - Statsig user object
{
"userID": "user_abc",
"email": "abc@company.com",
"customIDs": {
"org_id": "org_xyz"
},
"custom": {
"user_name": "Anna",
"user_region": "us-east",
"org_tier": "enterprise"
}
}
```
In Statsig, `email` is a top-level reserved field on the user object: place it directly as `email` (not `user_email`). Statsig expects `userID`, `email`, `ip`, and `userAgent` at the top level for user targeting and analytics.
## Deciding what to migrate vs. not
Before migrating flags from LaunchDarkly to Statsig, audit your existing flags and remove those that are no longer needed. Many organizations accumulate flag debt over time from stale flags, deprecated toggles, and legacy kill switches. Migration is an opportunity to start fresh with only active, valuable flags.
Use filters such as "Lifecycle" and "Type" in LaunchDarkly to identify which flags are worth importing into Statsig.
The following decision framework can help you decide which flags to import. The migration script follows this framework by default, but you can modify it as needed.
{% figure %}

{% /figure %}
## Importing flags into Statsig
Use the official import tool to import feature flags from LaunchDarkly into Statsig. The tool fetches flags from LaunchDarkly, translates them into Statsig's format, creates corresponding feature gates in Statsig, and tracks migration status and details in a CSV file.
Two options are available:
1. **[Open source script](/guides/open-source-script) (Recommended)**: Use this option to customize the integration logic. The script outputs a CSV of all your LaunchDarkly flags with migration status and URLs to the flag in LaunchDarkly and the gate in Statsig. This option imports all environments.
2. **[Statsig console](/guides/ui-based-tool)**: A UI-based wizard for importing LaunchDarkly feature flags and segments into Statsig. The wizard reports which gates and segments were migrated. This option imports only the production environment.
> If you are migrating from a different system, recreate flags manually in Statsig. After cleaning up in the previous step, you should have a smaller set of flags to migrate. Contact the Statsig team over email or Slack if you need assistance.
## Flipping evaluation from LaunchDarkly to Statsig
After your flags are imported into Statsig, update the evaluation logic in your application. Rather than replacing every LaunchDarkly evaluation call with a Statsig call at once, introduce a wrapper that supports gradual migration. This lets you run both systems in parallel, compare outputs, and switch over incrementally with minimal risk.
The wrapper approach provides several key benefits:
* **Parallel execution**: Run both systems simultaneously to validate behavior
* **Gradual rollout**: Migrate flags one at a time or by percentage
* **Easy rollback**: Quickly revert to LaunchDarkly if issues arise
* **Consistent interface**: Maintain existing application code structure
#### Implementation guide
**1. Before migration: LaunchDarkly Evaluation**
Here's what a typical LaunchDarkly setup might look like:
```javascript
import { LDClient } from 'launchdarkly-js-client-sdk';
const ldClient = LDClient.initialize('client-key', {
key: 'user_abc',
custom: { plan: 'pro' }
});
ldClient.on('ready', () => {
const isNewHomepageEnabled = ldClient.variation('new_homepage_flag', false);
const buttonColor = ldClient.variation('button_config', 'gray');
});
```
**2. Create the Migration Wrapper**
Create a comprehensive wrapper (`featureWrapper.js`) that handles both systems. The wrapper should check Statsig first, and if unavailable, fallback to LaunchDarkly:
```javascript
import { getLDClient } from './launchdarklyService';
import { getStatsigClient } from './statsigService';
export const wrapperFlags = {
// For boolean flags
wrapperGetFlag(flagKey, defaultValue = false) {
const statsigClient = getStatsigClient();
if (statsigClient) {
return statsigClient.checkGate(flagKey);
}
const ldClient = getLDClient();
if (ldClient) {
return ldClient.variation(flagKey, defaultValue);
}
return defaultValue;
},
// For dynamic configs (string, number, or json flags)
wrapperGetConfig(user, configKey) {
const statsigClient = getStatsigClient();
if (statsigClient) {
const config = statsigClient.getConfig(user, configKey);
return {
get: (paramKey, defaultValue) => config.get(paramKey, defaultValue)
};
}
const ldClient = getLDClient();
if (ldClient) {
return {
get: (paramKey, defaultValue) => {
const ldKey = `$\{configKey\}.${paramKey}`;
return ldClient.variation(ldKey, defaultValue);
}
};
}
return {
get: (_key, defaultValue) => defaultValue
};
}
};
```
**3. Refactor application code to use the wrapper**
After the wrapper is in place, route all flag checks through it. The application logic doesn't change: only the mechanism by which flags are retrieved changes.
```javascript
import { wrapperFlags } from './featureWrapper';
const user = {
userID: 'user_abc',
custom: { plan: 'pro' }
};
// Boolean gate
if (wrapperFlags.wrapperGetFlag('new_homepage_flag', user)) {
showNewHomepage();
}
// Multivariate config
const buttonConfig = wrapperFlags.wrapperGetConfig('button_config', user);
const buttonColor = buttonConfig.get('color', 'gray');
```
**4. Validate and gradually cut over**
After you have validated that Statsig is working as expected and that migrated flags return correct values, begin migrating more flags. Repeat the above steps for 2–3 engineering teams to confirm that different use cases are covered.
After flags have been maintained in both systems long enough to confirm stability, phase out LaunchDarkly.
1. **Remove LaunchDarkly fallback from the wrapper** - Update the wrapper functions to rely solely on Statsig. This simplifies the logic and ensures LaunchDarkly is no longer queried in production.
2. **Delete LD initialization logic and SDK imports** - Any references to `LDClient.initialize` or `ldClient.variation` can now be safely removed.
## How to run the migration process
Use a phased rollout so each team can adopt Statsig independently. This approach supports gradual migration, scoped validation, and shared learnings across the organization.
| Phase | Description | Who | Duration |
|-------|-------------|-----|----------|
| 1. Auditing existing flags in LaunchDarkly | Each team reviews their LaunchDarkly flags and identifies which flags are worth migrating | Individual Teams | Ongoing (2-3 days per team) |
| 2. Start creating all new flags in Statsig | Starting immediately, all new flags to be created in Statsig to avoid using legacy system out of habit | Org-wide | 1 week |
| 3. Pilot migration with one team | Select one team to migrate a small set of LaunchDarkly flags to Statsig using the wrapper. Validate that migrated flags work as expected | Pilot team | 1–2 weeks |
| 4. Org-Wide migration and cutover | Repeat migration for more teams in waves. Create training docs and guides for org wide adoption. Once Statsig is stable and adopted org-wide, remove LaunchDarkly fallback from wrapper | All teams + central guidance | 3–4 weeks (rolling) |
If you need additional assistance or want to discuss your specific case, contact Statsig.
================================================================================
# Open Source Script
URL: https://docs.statsig.com/guides/open-source-script
Updated: 2026-07-11
================================================================================
[This package](https://github.com/statsig-io/migrations) is designed to help automate migration of feature flags from LaunchDarkly to Statsig. It fetches feature flags from LaunchDarkly, translates them into Statsig's format, and creates corresponding feature gates in Statsig.
## Considerations
This script works by default. Start with a test environment of 5–10 flags. Before running the script at scale, consider the following:
* **IMPORTANT**: If you don't need to customize the import script, use [Statsig's in-console tool](/guides/ui-based-tool) instead.
* The script uses a tag `Imported from LaunchDarkly` to identify migrated flags in Statsig. Ensure this tag is unique and recognizable.
* The script includes a function to delete all Statsig feature gates with a specific tag. Use this with caution to clean up after a test or failed migration.
* The script requires API keys for both LaunchDarkly and Statsig. Keep these keys secure.
## Installation
To run the script, you need Node.js and npm installed on your system. Execute directly:
```bash
npx @statsig/migrations --from launchdarkly --launchdarkly-project-id default
```
## Configuration
* Provide your [LaunchDarkly API key](https://docs.launchdarkly.com/home/account/api) and [Statsig Console API key](/console-api/introduction) in the script
* Map LaunchDarkly environments to Statsig environments that aren't already the same by using `--environment-name-mapping` to the script.
* Map LaunchDarkly context kind to Statsig's custom unit ids and custom fields.
You can find the detailed instructions for these steps on the [Github repo](https://github.com/statsig-io/migrations).
## Running the script
To execute the migration script, run the following command in your terminal:
```bash
node index.js
```
The script performs the following actions:
1. Fetch all feature flags from LaunchDarkly.
2. Translate each flag into Statsig's format.
3. Create feature gates in Statsig.
4. Write the migration status and details to a CSV file named `flag_migration_tracker.csv`.
## Example translations
The following examples show how LaunchDarkly feature flags are translated into Statsig feature gates:
**Example 1:** LaunchDarkly flag with email and name targeting conditions
{% figure %}

{% /figure %}
**Example 2:** LaunchDarkly flag with country-based targeting (off flag)
{% figure %}

{% /figure %}
## Troubleshooting
If you encounter issues during the migration, check the following:
* Confirm that the API keys are correct and have the necessary permissions.
* Review the error messages in the console to diagnose the problem.
Pull requests and feedback are welcome.
================================================================================
# UI-Based Tool
URL: https://docs.statsig.com/guides/ui-based-tool
Updated: 2026-07-11
================================================================================
This guide covers how to use Statsig's built-in LaunchDarkly migration tool. This UI-based tool only imports the "production" environment.
## What you need
Review the full checklist in the [LaunchDarkly Migration Guide](/guides/migrate-from-launchdarkly#what-you-need), then gather:
1. Your project key. Projects in LaunchDarkly have a Name (for example, "My Mobile App") and a Key (for example, my\_mobile\_app).
2. A read-only access token for this project. Create one in LaunchDarkly under Account Settings > Authorization and limit the scope to read-only.
3. A Statsig project. Try this in a test project first.
## How it works
These screens mirror the [console walkthrough](/guides/migrate-from-launchdarkly#how-it-works):
1. If you don't have any feature gates in your project, Statsig prompts you to import Feature Gates.
{% figure %}

{% /figure %}
2. Select LaunchDarkly as the platform you want to migrate from.
{% figure %}

{% /figure %}
3. Enter your LaunchDarkly Project Key and API Key/access token.
{% figure %}

{% /figure %}
4. Preview the migration summary. The tool highlights which gates can and can't be migrated. Gates that aren't migrated include those with segments (coming soon) and those with non-Boolean flags.
{% figure %}

{% /figure %}
5. Finish migration of the gates. All migrated gates are tagged "Migrated" so you can identify them.
{% figure %}

{% /figure %}
================================================================================
# FAQ
URL: https://docs.statsig.com/faq
Updated: 2026-07-11
================================================================================
## SDKs and APIs
### How does bucketing in the Statsig SDKs work?
Refer to [How Evaluation Works](/sdks/how-evaluation-works).
***
### Can I add a layer to a running experiment?
No. Layers are fixed after an experiment starts to preserve the integrity of results. Statsig may support editing layers in the future.
***
### Can I rename an existing experiment or feature gate?
Yes. When renaming an entity (for example, a feature gate, experiment, or layer), only its display name changes. The underlying ID referenced in your code remains unchanged because IDs are immutable, which prevents breaking existing implementations.
***
### Why define parameters instead of reading the experiment group?
Parameters let you iterate quickly without code changes and support richer experiment setups. For example:
```js
// Group-based approach — requires code changes for each variant
if (otherEngine.getExperiment('button_color_test').getGroup() === 'Control') {
color = 'BLACK';
} else if (otherEngine.getExperiment('button_color_test').getGroup() === 'Blue') {
color = 'BLUE';
}
// Statsig parameter approach — variants can be changed from the console
const color = statsig.getExperiment('button_color_test').getString('button_color', 'BLACK');
```
***
### Why aren't exposures or custom events showing up?
In short-lived environments (scripts, edge workers), the process may exit before events flush. Call `statsig.flush()` before shutdown. For more detail, refer to the [Node.js server SDK docs](/server/nodejsServerSDK#flushing-events).
***
### My SDK language isn't listed. Can I still use Statsig?
Likely yes. Post in the Statsig Slack community and the team will discuss options.
***
### How do I retrieve all exposures for a user?
The [Users tab](https://console.statsig.com/users) shows historical exposures. For hypothetical assignments (e.g., to bootstrap clients) you can call `getClientInitializeResponse` on the server. Pass `{ hash: 'none' }` if you need readable keys:
```js
const assignments = statsig.getClientInitializeResponse(user, 'client-key', { hash: 'none' });
```
***
### What happens if I check a config that doesn't exist?
The SDK returns defaults: `false` for gates and the supplied fallback for experiments/layers. The evaluation reason shown is `Unrecognized`. Refer to [SDK debugging](/sdks/debugging#evaluation-reason) for more detail. This applies to deleted, archived, or unseen configs (for example, configs filtered by [target apps](/sdks/target-apps)).
***
## Feature gates
### If I change the rollout percentage, do existing users keep their result?
Yes. Increasing the pass percentage (for example, 10% to 20%) keeps the original 10% and adds new traffic until the new percentage is reached. Decreasing it removes the newest slice first. To reshuffle all users, you must resalt the gate. Experiments behave differently; use targeting gates for deterministic control.
***
## Statistics
### What statistical tests does Statsig use?
Statsig uses a two-sample Z-test for most experiments and [Welch's t-test](/experiments/statistical-methods/p-value#welchs-t-test) when sample sizes are small or variances differ.
***
### How does Statsig handle low sample sizes?
Statsig falls back to [Welch's t-test](/experiments/statistical-methods/p-value#welchs-t-test) and offers CUPED/winsorization to boost power.
***
### When should I use one-sided vs. two-sided tests?
Use one-sided when you only care about movement in a single direction; it increases power but hides movement in the opposite direction.
***
## Experimentation
### How do I get started with an A/B test?
If the feature isn't live yet, wrap it in a [feature gate](/guides/first-feature) and roll out. If it's already in production, create an [experiment](/guides/abn-tests). Results appear in the Pulse view.
***
### Can I target experiments to specific users (e.g., iOS only)?
Yes. Use a feature gate with targeting rules as a pre-filter for your experiment.
{% figure %}

{% /figure %}
***
## Billing
### What counts as a [billable event](https://statsig.com/pricing#faq)?
Any gate/experiment check or event logged through the SDK or APIs. Pre-computed metrics and custom metrics based on existing data also count.
***
### How do I monitor and manage billable volume?
1. Export usage from the **Usage and Billing** tab.
2. Pivot by event to identify heavy hitters.
3. Admins receive alerts at 50/75/100% of contract.
{% figure %}

{% /figure %}
***
### How many projects can I create with a Pro subscription?
Each Statsig Pro plan unlocks one project with pro features and 5M events. Additional projects require their own upgrade.
Enterprise plans can cover multiple projects. [Contact us](https://statsig.com/contact/demo) to discuss options.
***
## Platform usability
### When should I create a new project?
Projects have distinct boundaries. If you're using the same userIDs and metrics across surfaces, apps or environments, put them in the same project. Create a new project when you're managing a separate product with unique user IDs and metrics.
For example, if you have a marketing website (anonymous users) and a product (signed-in users), you may want to separate them. However, if you want to track success across both you should manage them in the same project (for example, from user signup on the marketing website to user engagement within the product).
Reasons to avoid creating a new project:
* Segregating by environment: Statsig supports environments natively, and you can customize them. You can turn features or experiments on and off by environment.
* Segregating by platform: If you have an iOS app and a web app, collecting data in the same project lets you view data by platform and understand whether you increased the overall metric or moved users from one platform to the other.
***
### How can I monitor the health of Statsig and get support?
You can check the live operational status of Statsig at status.statsig.com. To report an issue or receive help, reach out directly in our Slack Community.
***
================================================================================
# About Warehouse Native
URL: https://docs.statsig.com/statsig-warehouse-native/introduction
Updated: 2026-07-11
================================================================================
{% callout type="note" %}
Warehouse Native is part of Statsig's Enterprise tier. [Contact us](https://statsig.com/contact/demo) to get started.
{% /callout %}
Statsig Warehouse Native is an enterprise-grade experimentation platform that runs analysis in your data warehouse. It integrates easily with your existing datasets and any source of experiment assignment data – including a powerful integration with Statsig's SDKs and real-time logging infrastructure.
Warehouse Native shares core features with Statsig Cloud but focuses on specific scenarios around running experiments on top of your warehouse.
### Use cases for Statsig Warehouse Native
* End-to-end experimentation platform covering targeting, experiment setup and assignment, and analysis.
* Features include Feature Flagging, Automated/Protected Rollouts, Native Holdouts, Mutual Exclusion, and comprehensive analysis tools.
* Modern statistical engine for running analyses on existing experiments from third-party or internal systems.
* Statsig offers a full suite of experiment measurement tools, including CUPED, Stratified Sampling, Switchback Tests, and more.
If you aren't sure whether Statsig Warehouse Native or Statsig Cloud is a better fit for your experimentation needs, reach out to the support team, your sales contact, or the [Slack community](https://statsig.com/slack) for guidance.
## How Warehouse Native works
Statsig Warehouse Native runs experimentation compute jobs directly in your data warehouse, using your existing datasets to calculate metrics and enrich experiment analysis based on your data.
{% figure %}

{% /figure %}
### SDK integration
Using [Statsig SDKs](/sdks/getting-started) with Statsig Warehouse Native is similar to integrating with Statsig Cloud:
1. **Set Up Targeting and Experiments**: Create and manage experiments using the Statsig console.
2. **Initialize the SDK**: Integrate the Statsig SDK on your client or server-side applications.
3. **Targeting and Assignment**: Call the Statsig SDK to assign users to variants.
* Optionally provide a logging callback to store logs in your warehouse or use Statsig's real-time infrastructure for instant diagnostics and safe rollouts.
The resulting assignment and (optional) event logging data ends up in your warehouse, where you can connect it to other datasets for analysis. If you log to Statsig, data is exported on-demand for real-time analysis or in scheduled batch jobs.
### Experiment setup
Running experiments on Warehouse Native involves several key steps:
1. **[Connect Statsig to Your Warehouse](/statsig-warehouse-native/guides/quick-start#step-1-connect-your-warehouse)**: Integrate Statsig with your data warehouse to access relevant datasets.
2. **Create Metrics**: Define the input data and configure experiment metrics.
3. **Log Exposures**: Log experiment exposures with Statsig or point to existing assignments in your warehouse.
4. **State Your Hypothesis**: Formulate a hypothesis and choose scorecard metrics to evaluate the experiment.
5. **Run Pulse Analysis**: Execute a Pulse analysis to test your hypotheses and measure the impact on scorecard metrics.
### Advanced analysis tools
Statsig’s [data analysis tools](/experiments/interpreting-results/read-results) run directly in your data warehouse. All queries, intermediate datasets, and final results generated by Statsig are available in your warehouse for auditing and custom analysis.
* **Pulse Analysis**: Pulse helps you test your hypotheses and measure the impact of changes on your scorecard metrics.
* **Metrics Explorer**: Visualize experiment metrics for your entire population and drill down into user behavior. Metrics Explorer is part of a broader suite of product observability tools, including Session Replay, advanced filtering, and custom dashboards.
* **Exposure Analysis**: Analyze your experiment's first exposures to debug and gain insights on your experiments in Metrics Explorer. Drill down into certain properties or understand how your experiments interact with one another.
## Start using Warehouse Native
{% callout type="info" %}
Reach out to the support team, your sales contact, or the [Slack community](https://statsig.com/slack) with any questions.
{% /callout %}
Use the following guides to start running or analyzing an experiment:
{% card-grid %}
{% card title="Quick Start" href="/statsig-warehouse-native/guides/quick-start" icon="speed" %}
Connect Statsig to your warehouse and get test results in minutes
{% /card %}
{% card title="Evaluate Statsig" href="/statsig-warehouse-native/guides/running_a_poc" icon="warehouse" %}
Evaluate how Statsig Warehouse Native can work for you
{% /card %}
{% card title="Learn About Experimentation" href="/statsig-warehouse-native/features/statistics" icon="science" %}
Learn about Statsig's modern Stats Engine
{% /card %}
{% /card-grid %}
***
================================================================================
# Warehouse Native Quickstart
URL: https://docs.statsig.com/statsig-warehouse-native/guides/quick-start
Updated: 2026-07-11
================================================================================
This page walks you through connecting your data, configuring a metric, and getting experiment results with Statsig Warehouse Native.
All you'll need is a table in your warehouse that has metric or event logging data.
## Step 1: Connect your warehouse
Statsig uses your warehouse to store and analyze your experiment data; you have total control and visibility over the data itself. To connect your warehouse, go to your warehouse's setup page.
* [Snowflake](/statsig-warehouse-native/connecting-your-warehouse/snowflake)
* [Athena](/statsig-warehouse-native/connecting-your-warehouse/athena)
* [BigQuery](/statsig-warehouse-native/connecting-your-warehouse/bigquery)
* [Databricks](/statsig-warehouse-native/connecting-your-warehouse/databricks)
* [Redshift](/statsig-warehouse-native/connecting-your-warehouse/redshift)
## Step 2: Connect to data
To connect your event or metric data, you'll create a [Metric Source](/statsig-warehouse-native/configuration/metric-sources). Go to the *Metric Sources* page and click **Create** to make a new Metric Source.
{% figure %}

{% /figure %}
If you have a table, use the `Table` metric source type and put in the path to your table. Otherwise, you can write a query to access or generate some test data.
Press **Analyze** to generate samples from your table, then map required columns (Timestamp and UserID) so Statsig can connect your metric data to your assignment data.
{% figure %}

{% /figure %}
Save your changes, and you've connected to your data!
## Step 3: Make a metric
Now that you've connected to data, you can build metrics on top of this data. You can configure this programmatically later; for now, go to your *Metrics Catalog* and click **Create** to make a new metric.
{% figure %}

{% /figure %}
Point to the metric source you configured, name your metric, and press **Create**. This takes you to the new metric's page, where you can configure your metric.
To get started, Statsig recommends making a [count metric](/statsig-warehouse-native/metrics/count). A count metric counts the number of rows in your metric source. This is useful for event logging. For example, you might filter to a "purchase" event to count the number of times users purchased an item.
Select "Count" and save - or, feel free to pause here and explore the options here.
## Step 4: Connect an experiment
Next, you'll connect to experiment data. If you have a table with exposures you've already logged, use that. Ensure you've logged the same identifier there as you used in your metric source.
Otherwise, you can quickly follow the guide to setting up an [A/A test](/experiments/types/aa-test), using the same data you used for your exposures. This should generate a neutral experiment result.
Go to *Assignment Sources* and create a new one.
{% figure %}

{% /figure %}
If using an AA test, follow the instructions to randomly assign users to groups. If you're using existing assignments, write a query to pull the data from your logs and map the unit, group, experiment, and timestamp columns.
{% figure %}

{% /figure %}
Pressing **Save and Scan** saves your new source and detects experiments that exist on the source. This takes seconds to a few minutes; once it's done, scroll down to look through the experiments Statsig found.
{% figure %}

{% /figure %}
## Step 5: Analyze your experiment
Press **Create** on your experiment of interest to start creating your experiment.
{% figure %}

{% /figure %}
Add a display name and hypothesis, then press **Create**. This takes you to the final setup step, where you specify the metrics for your experiment. Choose the metric you created in step 3.
Statsig automatically detects the group split, but if the detected split is incorrect you can manually adjust it to the intended value.
{% figure %}

{% /figure %}
Press **Save and Analyze**, and Statsig starts calculating Pulse Results. You can track the progress in the loading bar at the bottom of the experiment's results page.
## Step 6: Read results
If everything worked, you should see:
* Your hypothesis. This lives at the top of the results page to give context and guide interpretation of the results.
* Cumulative exposures. This shows you the number of unique units exposed to each group, and the balance between groups
* Your scorecard. This shows a quick summary of the observed differences in metrics between your experiment groups, with access to additional views and raw statistics
{% figure %}

{% /figure %}
Explore the product and docs to learn more about these features:
* Click into a result's error bar to view raw statistics, timeseries, and projected [timeline impact](/experiments/statistical-methods/topline-impact)
* Hover over a metric to get detailed context on its inputs and how the pulse result was calculated
* Go to the *Diagnostics* tab to view the [checks](/statsig-warehouse-native/features/monitor-an-experiment) Statsig automatically ran to validate your experiment results.
* Select the reloaded timestamp to view the run time and query cost of your Pulse analysis, and the SQL queries used to calculate the results.
* Go to the *Explore* tab to start filtering data, exploring results by dimensions, or running other follow-up analyses.
* Go to the *Summary* tab to start putting together a [report](/statsig-warehouse-native/features/reports) to share the results of your analysis.
* Start a discussion or add context, either in the *Discussion* tab or with in-context comments on top of the results themselves.
The example experiment was not an AA test, and there was an experimental impact. The result is statistically significant, with an estimated lift of +12.89% ±0.93% from control to test. Select the result to view additional details, such as the expected change to the overall topline metric value if the experiment ships.
{% figure %}

{% /figure %}
You've completed the Quick Start guide.
================================================================================
# Comparing Warehouse Native and Cloud
URL: https://docs.statsig.com/statsig-warehouse-native/native-vs-cloud
Updated: 2026-07-11
================================================================================
Statsig Warehouse Native and Statsig Cloud share many capabilities, but there are differences between the platforms. This page describes those differences to help you choose the right product for your experimentation needs.
If a feature is critical for your evaluation, contact the support team, your sales contact, or the [Slack community](https://statsig.com/slack).
## Feature gates
| Feature | Cloud vs. WHN Status |
| ------------------------- | -------------------- |
| Targeting | Identical |
| Overrides | Identical |
| SDKs | Identical |
| Diagnostics | Identical |
| Pulse | Identical |
| Gate lifecycle management | Identical |
## E2E experiments (with SDK)
| Feature | Cloud vs. WHN Status |
| ------- | ---------------------------- |
| Pulse | WHN offers on-demand reloads |
## Pulse (scorecards)
| Feature | Cloud vs. WHN Status |
| ----------------------- | ------------------------------------------------ |
| CUPED | Identical |
| CURE | WHN only for now |
| Topline Impact | Identical |
| Daily Time Series | Identical |
| Frequentist vs Bayesian | Identical |
| Sequential Testing | Identical |
| BF Correction | Identical |
| Power Analysis | Identical |
| Custom Queries | Identical |
| Guardrail Metric Alerts | Available in WHN, based on your pipeline cadence |
| Export to CSV | Identical |
| Share Links | Identical |
## Analysis only experiments (without SDK)
| Feature | Cloud vs. WHN Status |
| --------------------------- | -------------------- |
| Overrides | Not relevant w/o SDK |
| Diagnostics (SDK Related) | Not relevant w/o SDK |
| Diagnostics (Data specific) | WHN has additional |
| Capture Hypothesis | Identical |
| Capture Images | Identical |
| Experiment Groups | Identical |
| Experiment Parameters | Not relevant w/o SDK |
| SRM Checks | Identical |
| View SQL | WHN-only |
## Core features
| Feature | Cloud vs. WHN Status |
| -------------- | -------------------- |
| Layers | Identical |
| Dynamic Config | Identical |
| Dashboards | Identical |
| Segments | Identical |
| Holdouts | Identical |
## Insights
| Feature | Cloud vs. WHN Status |
| ------------------------- | -------------------- |
| Experiment Timeline | Identical |
| Knowledge Bank | Identical |
| Metric Insights | Identical |
| Metric Correlations | Identical |
| Metric Impacts | Identical |
| Active feature analysis | Identical |
| Historic feature analysis | Identical |
## Metrics
| Feature | Cloud vs. WHN Status |
| ----------------------------------------- | -------------------- |
| Metrics Explorer | Substantially Identical |
| SDK Events Logstream | Identical |
| Autocreate metric from SDK Events | Cloud only for now |
| User Accounting Metrics (DAU, Stickiness) | Cloud only for now |
| Automated A/A Tests | Cloud only for now |
| Entity Property Source ID mapping | WHN only |
## Organization & Settings
| Feature | Cloud vs. WHN Status |
| ----------------- | -------------------- |
| SSO | Identical |
| Experiment Policy | Identical |
| Change Reviews | Identical |
| Custom UnitIDs | Identical |
| Custom RBAC | Identical |
## Integrations & admin
| Feature | Cloud vs. WHN Status |
| ---------------------------------------- | -------------------- |
| Change mgmt (Slack, Datadog, Github etc) | Identical |
| CDBs (Segment/Rudderstack etc) | Not relevant for WHN |
| Custom Environments | Identical |
| Target Applications | Identical |
| Console API | Identical |
| Audit Log | Identical |
| Audit Log API | Identical |
| Autotune (MAB) | Identical |
| Users Tab | Identical |
================================================================================
# Running a Warehouse Native POC
URL: https://docs.statsig.com/statsig-warehouse-native/guides/running_a_poc
Updated: 2026-07-11
================================================================================
## What Statsig Warehouse Native does
**Statsig Warehouse Native** enables customers with existing metric logs to run analysis on their existing metric data, and optionally bring previous assignment data or offline experiments into the platform. Statsig WHN has two types of experiments:
**Assign and Analyze**: You can run an experiment on **web/mobile/app** and use Statsig’s SDKs to assign (bucket or randomize) users, and then analyze results.
**Analyze**: You can run an experiment elsewhere (***your own SDK, email, direct mail, sms, ivr, etc.***) and use Statsig to analyze that data and calculate experiment results. Statsig reads this assignment data from your warehouse in [this format](/statsig-warehouse-native/configuration/assignment-sources#example-data); Statsig calls these [Assignment Sources](/statsig-warehouse-native/configuration/assignment-sources).
If you have a pre-existing experiment in your warehouse, start with an **Analyze** experiment. This is an effective way (less than 1 day) to establish a connection between Statsig and your warehouse, and to experience [experiment results](/experiments/interpreting-results/read-results) in the Statsig console. After that, run an **Assign and Analyze** experiment using Statsig's SDKs (typically an A/A test). The A/A **Assign and Analyze** experiment lets you test Statsig's SDKs and implementation process with your engineering and product team.
## Steps to running an effective proof of concept with Warehouse Native
{% figure %}

{% /figure %}
Follow these steps when planning your Warehouse Native implementation:
1. **Define your experiment(s) and metrics for validation** - A proof of concept determines if Statsig fits your experimentation needs; running an experiment is the most direct path for evaluation. **Responsible Party**: Typically a product or engineering lead.
- Plan to run 1-2 production level experiments to validate. Past experiments, A/A tests or upcoming projects or product changes are great opportunities to implement a Statsig experiment!
- Identify your hypothesis and [metrics](/statsig-warehouse-native/configuration/metrics) to validate this hypothesis. Statsig joins these metrics with unit assignment/exposure data and runs them through the stats engine.
- If your team plans on running **analysis only**, identify the user **assignment** data that Statsig joins with the metric data.
- This approach can yield results for analysis in as little as **30 minutes,** assuming data is readily available for ingestion
- If your team plans on using the **Assign and Analyze** experimentation option, identify **where** the experiment will run. Typically, **web-based** experiments are easier to evaluate, but Statsig also has SDK support for server and mobile SDKs.
- **Note**: It’s important the implementing team understands how the SDKs operate before executing a proof of concept. The [client](/client/introduction) and [server](/server/introduction) docs can help orient your team.
- A typical evaluation takes **2-4 weeks** to account for experiment design, implementation, time to bake, and analysis. To ensure a successful POC, [have a well scoped plan](/statsig-warehouse-native/guides/running_a_poc#2-phase-0-scope--prepare-your-poc) and ensure the right teams are included to assist along the way.
- Read [experimentation best practices](https://statsig.com/blog/product-experimentation-best-practices) to get an idea of how to best succeed.
2. **Connect the warehouse** - To query data and operate within your warehouse, you’ll need to allocate resources and connect to Statsig. You may choose to utilize an existing prod database or create a separate cluster specifically for experimentation (if you don’t already have one).
- Statsig requires a role and the following access:
- Read access to metric and exposure data
- Write access so Statsig can write results and exposures back to the warehouse
- Access to run jobs and query data
- Find more guidance on [connecting with your specific warehouse vendor here](/statsig-warehouse-native/guides/connect).
- Review the [data pipeline overview](/statsig-warehouse-native/analysis-tools/pipeline-overview) to understand how data flows for warehouse native jobs.
3. **Connect metric sources and define metrics** - Once the data warehouse has been connected, you can begin defining metric and assignment sources (if applicable) in Statsig. Statsig expects specific schemas to correctly map the data to its pipelines:
{% figure %}

{% /figure %}
Beyond these columns, the schema is flexible and can accept additional columns. You can use **Metadata** to filter metrics and perform more granular analysis.
- Review guides for creating a [metric](/statsig-warehouse-native/configuration/metric-sources) and [assignment](/statsig-warehouse-native/configuration/assignment-sources) source
- Follow the [data best practices](/statsig-warehouse-native/guides/best-practices) to ensure your queries are running efficiently.
{% callout type="info" %}
This section is important to review and can prevent unnecessary infrastructure costs!
{% /callout %}
After metric sources have been connected, configure [metrics](/statsig-warehouse-native/configuration/metrics) to perform various aggregations (e.g., Sum, Mean, Count, Unique Users) that represent what you’re trying to measure in your experiments.
- [Supported metric types](/statsig-warehouse-native/configuration/metrics#metric-types) and ways to [configure them](/statsig-warehouse-native/configuration/metrics)
- Use [cohort metrics](/statsig-warehouse-native/features/cohort-metrics) to measure impact during a certain time frame per user.
4. **Create and roll out an experiment** - In step 1, you defined the planned experiment(s) and the metrics used to validate them. With metrics and assignment sources configured, you can now create the experiment(s). Refer to the [detailed guide for experiment setup](/statsig-warehouse-native/guides/experiments), and consider these things as you complete this step:
- Create your hypothesis and select the **experiment (assignment) source**
- If using the SDK for assignment, the SDK itself serves as the assignment source.
- [Custom IDs](/guides/experiment-on-custom-id-types) can be used but must first be configured; for example: `device_id`, `vehicleId`.
- Refer to [advanced settings](/statsig-warehouse-native/features/configure-an-experiment#advanced-settings) for the many ways you can configure your experiment
- As you roll out your experiment, you can [monitor the status with health checks](/statsig-warehouse-native/features/monitor-an-experiment) and get a readout of live exposures as they come through the SDK.
- If you’re hoping to quickly validate the platform, you can create and run a [quick A/A test](/statsig-warehouse-native/guides/aatest).
5. **Read results** - Once the experiment has successfully run, read the results and ensure everything looks reasonable. Was your hypothesis validated or are the results surprising? Are the results easy to interpret and navigate for the teams involved? Refer to the [section on Pulse](/statsig-warehouse-native/guides/pulse) to get an idea of the high-level analytics capabilities. A few things to note:
- You can slice results further using the [*Explore* tab](/statsig-warehouse-native/guides/pulse#explore), which lets you break down results by specific user and event properties.
- You can configure exposure and metric data to be forwarded to your warehouse.
- The [*Health Checks* (diagnostics) tab](/statsig-warehouse-native/guides/pulse#health-checks) surfaces the SQL used to generate results so you can validate any analysis performed on your systems.
6. **Finalize evaluation and next steps:** Ultimately, a POC validates a set of evaluation criteria that determines whether Statsig is a good fit for your team’s workflows. The following graphic provides high-level guidance on what to look for during your evaluation phase.
{% figure %}

{% /figure %}
To move forward, convert your POC environment to a production-level implementation. Refer to the [production guide](/statsig-warehouse-native/guides/production) for details.
Statsig support is available through the [community Slack channel](https://statsig.com/slack).
================================================================================
# How to Run a Playground Evaluation
URL: https://docs.statsig.com/statsig-warehouse-native/guides/playground_eval
Updated: 2026-07-11
================================================================================
{% callout type="info" %}
Playground evaluations are only recommended for Enterprise-tier customers.
{% /callout %}
Many customers evaluating Statsig have sensitive data and long lead times to test on production data due to privacy and data security requirements. Statsig offers a read-only [Demo Project](https://console.statsig.com/whn_demo), but the demo project doesn't let you fully experience the warehouse native product.
To get full access, contact support to request a Playground Evaluation project. This is a project hosted by Statsig with sample, near-real-time data sources that you can use to set up experiments, analyze metrics, and evaluate Statsig internally. You'll be an admin and can add teammates to explore the shared project together.
## Playground projects
Playground projects come with some boilerplate connections hooked up:
* A real-time event streaming metric source
* A daily user-level metric source
* An assignment source with a handful of experiments
* A handful of pre-loaded metrics and experiments
{% callout type="note" %}
Playground projects don't come with any assign-and-analyze experiments, so some features that require SDK usage (e.g. Stratified Sampling) aren't available. You can use a normal demo project with an SDK on a local script to generate data for those use cases.
{% /callout %}
## Evaluation
Follow steps 2 and later from the [quick start](/statsig-warehouse-native/guides/quick-start) guide, starting with the tables available to the playground project. This gives you a sense of what running experiments and managing metrics in Statsig involves.
After completing those steps, refer to the [POC guide](/statsig-warehouse-native/guides/running_a_poc) and simulate the steps relevant to your evaluation. Exploring the Pulse UI together has been useful for teams. The Statsig team welcomes any questions or feedback.
================================================================================
# Working With the SDK
URL: https://docs.statsig.com/statsig-warehouse-native/guides/sdks
Updated: 2026-07-11
================================================================================
Warehouse Native works with any of Statsig's SDKs for logging events and getting feature flags or experiment assignments.
This page is a brief overview of how Warehouse Native works with Statsig's SDKs. Refer to the [client](../../client/introduction) or [server](../../server/introduction) SDK docs for help setting up SDKs.
## Data Forwarding
When you first set up your data connection, Statsig creates tables to forward datasets to and generates assignment and metric sources that include any user-level metadata fields you log as part of your evaluation.
You can find the configuration for these datasets and the table Statsig creates or outputs data to in the advanced section of the warehouse connection page.
{% figure %}

{% /figure %}
## Exposures
Statsig calculates deduplicated first-exposure rollups for you on a daily basis and exports that miniaturized dataset to your warehouse. Additionally, on every Pulse load on the first day of an experiment, Statsig exports deduplicated exposures for the day to your warehouse in near real-time, up to 1 million exposures.
## Log Events
You can also use Statsig's powerful event logging to send events to the Statsig SDK; Statsig later exports them to your warehouse for analysis.
================================================================================
# Synthetic A/A Test
URL: https://docs.statsig.com/statsig-warehouse-native/guides/aatest
Updated: 2026-07-11
================================================================================
To create a quick A/A test, randomly split existing users you already have events for. Create an assignment source using the script below and you'll be ready to analyze your A/A test in minutes and see Pulse scorecards populate.
Example script:
```sql
SELECT
user_id,
timestamp,
'AA_Test_1' AS experiment_name, --CAST('AA_Test_1' as varchar) AS experiment_name for Redshift warehouse
CASE
WHEN THEN 'Control'
ELSE 'Test'
END AS GroupAssignment
FROM
```
Replace `` with the following based on your warehouse:
* Bigquery: `mod(abs(farm_fingerprint(cast(user_id as string))), 100) < 50`
* Redshift: `mod(abs(farmFingerprint64(cast(user_id as varchar))), 100) < 50`
* Snowflake: `mod(abs(hash(cast(user_id as string))), 100) < 50`
* Databricks: `mod(abs(hash(cast(user_id as string))), 100) < 50`
* Athena: `mod(abs(cast(conv(substr(md5(cast(user_id as varchar)), 1, 16), 16, 10) as bigint)), 100) < 50`
================================================================================
# Running Email Experiments
URL: https://docs.statsig.com/statsig-warehouse-native/guides/email-experiments
Updated: 2026-07-11
================================================================================
Email marketing experimentation is a common use case that Statsig Warehouse Native handles by analyzing the downstream outcomes of an email experiment.
This guide covers the key considerations for configuring this type of experiment.
### Data access offered by your marketing platform
> Does the platform offer the ability to ETL email engagement events (sends, deliveries, email opens) to your data warehouse either directly, or through a CDP or other pipeline?
Data access may be critical to the viability and quality of your experiment, depending on your specific use case. In particular, `email open` events are useful for controlling the population of users Statsig counts for analysis.
For example, if you send 20,000 emails and only 12,000 are delivered and opened, counting only the 12,000 email-open users in the test population (rather than the entire recipient list) avoids diluting the population and achieves more statistical power.
Use email open events to manually create an assignment source for an `Analyze Only` experiment, or to [filter your analysis population](/statsig-warehouse-native/configuration/qualifying-events/#filter-by-qualifying-events) for an `Analyze Only` experiment.
### Configuring scorecard metrics
> What are your key down-funnel (for example, on-site or in-app) success metrics for the experiment, and can you unify email recipients with their subsequent down-funnel interactions?
Email open events are useful for filtering the test population or for monitoring purposes, but aren't typically a primary metric unless the experiment is testing different subject lines.
Consider which down-funnel metrics to include as primary metrics for deciding on your experiment. Attributing down-funnel interactions that occur before user login can be complex, but [ID resolution in Statsig](/statsig-warehouse-native/features/id-resolution/) solves this.
Down-funnel metrics that occur after login are easy to attribute to the experiment, because you'll have the user ID and email address to map directly to the identifiers on your assignment source.
### Designing test groups and running the test
> What options does your email platform offer for defining test and control groups? Does the platform require you to upload a list of users for each group, or does it natively support A/B cohort definitions?
If your platform offers native A/B split capabilities, configure your test as an `Analyze Only` experiment in Statsig. Select **Analyze** and leave the **Experiment Completed** checkbox unchecked. Statsig then continues computing results from the first exposure time until you decide to conclude the experiment.
If you need to manually create the test groups, set up an `Assign and Analyze` experiment and use Statsig's SDKs to generate your test and control lists. Loop over all intended recipients in code and call `getExperiment`. This captures each test group assignment and automatically writes the assignment events to your data warehouse through the [Statsig managed exposure pipeline](/statsig-warehouse-native/connecting-your-warehouse/forwarded-data). Select **Start** before running your assignments script, then keep the test in a running state until you decide to conclude the experiment.
================================================================================
# Running Analysis Across Unit Types, AKA Cluster Experiments
URL: https://docs.statsig.com/metrics/different-id
Updated: 2026-07-11
================================================================================
There are two common scenarios where the experiment assignment unit differs from the analysis unit:
1. Measuring session-level metrics for a user-level experiment. Ratio metrics are commonly used to solve this (covered on this page).
2. Measuring logged-in metrics (eg. revenue) on a logged-out experiment. There are two solutions:
a. Running the experiment at the [device-level](/guides/first-device-level-experiment), with device-level metrics collected even after the user is logged-in.
b. Using [ID resolution](/statsig-warehouse-native/features/id-resolution).
This page explains how to set up the first scenario using Warehouse Native.
{% figure %}

{% /figure %}
## Example: Organizations and Users
Scenario:
* Your metrics source has both `org_id` and `user_id`.
* The relationship between `org_id` and `user_id` is 1-to-many. A single `org_id` can be associated with multiple users (`user_id`), but a `user_id` is only associated with a single `org_id`.
* Statsig assigns your experiment at the `org_id` level.
* You are interested in understanding the treatment effect at the `user_id` level, such as revenue per user.
### 1. Set up the metric source with `org_id` as an ID type
* In this table, each row of data should have both `org_id` and `user_id`.
{% figure %}

{% /figure %}
### 2. Choose your assignment source, where the unit of assignment is `org_id`.
{% figure %}

{% /figure %}
### 3. Define your metric of revenue per `user_id`
* Your denominator should be `count distinct user_id` instead of `unit count`, because the latter is equivalent to `count distinct org_id` in an `org_id` level experiment.
{% figure %}

{% /figure %}
### 4. Set up the experiment with `org_id`
{% figure %}

{% /figure %}
## How the Stats Engine handles cluster experiments
The Stats Engine uses the delta method to calculate variance and confidence intervals.
* For mean metrics, the Stats Engine records the number of observations per exposed unit in the records column of the staging data. This value acts as the denominator or cluster-size value for delta calculations.
* For general ratio metrics, the Stats Engine tracks the two-component metrics (the ratio and the denominator) as independent metrics, then combines them during the pulse analysis to derive a single metric.
For more information about the delta method, go to [Statsig - Delta Method Methodology](/experiments/statistical-methods/methodologies/delta-method). The delta method accounts for the covariance between the numerator and the denominator (more users per org is correlated with more revenue). Refer to section 3 of [this paper](https://alexdeng.github.io/public/files/kdd2018-dm.pdf) for details.
This approach is also relevant for analyzing event-level outcomes, such as average purchase value, where randomization occurs at the user level, and each user may experience multiple session events.
================================================================================
# Setup Checklist
URL: https://docs.statsig.com/statsig-warehouse-native/guides/checklist
Updated: 2026-07-11
================================================================================
After you've connected your warehouse and set up both metrics and assignment sources, verify your setup by checking the following items:
1. Primary keys
2. Timestamps
3. Duplication
4. Data availability
After completing these checks, your offline results should align with those in the Statsig console when advanced features are disabled.
## 1. Primary keys
When setting up an experiment, you can select the unit of assignment, acting as the primary key to join the assignment with metrics. The assignment source and the metrics source must use the same primary key.
In an Analyze-Only experiment, this primary key can be selected from the unit IDs defined by your assignment source.
* Ensure the unit ID in your assignment source matches the unit ID in your metrics source.
In an Assign and Analyze experiment, the Statsig SDK generates the primary key (unit ID).
* You can verify this unit ID in the statsig\_forwarded\_exposures table within the assignment sources.
* You must either forward the unit ID to the SDK ([docs](/client/introduction)) or use the SDK to manage your features and correspondingly generate the metrics table.
## 2. Timestamps
Analyze metric data only after a user has been exposed to the experiment. Pre-experiment data has no average treatment effect, so including it dilutes results. Statsig uses a timestamp-based join for this purpose, with an option for a date-based join for daily data. The join should look like the SQL snippet below:
```sql expandable
WITH
metrics as (...),
exposures as (...),
joined_data as (
SELECT
exposures.unit_id,
exposures.experiment_id,
exposures.group_id,
metrics.timestamp,
metrics.value
FROM exposures
JOIN metrics
ON (
exposures.unit_id = metrics.unit_id
AND metrics.timestamp >=
exposures.first_timestamp
)
)
SELECT
group_id,
SUM(value) as value
FROM joined_data
GROUP BY group_id;
```
## 3. Exposure duplication
Exposure data must be deduplicated before joining to ensure a single record per user. Many vendors also manage crossover users (users present in more than one experiment group) by removing them from analysis or alerting when this occurs with high frequency.
```sql expandable
SELECT
unit_id,
experiment_id,
MIN(timestamp) as first_timestamp,
COUNT(distinct group_id) as groups
FROM
GROUP BY
unit_id,
experiment_id,
group_id
HAVING COUNT(distinct group_id) = 1;
```
## 4. Data availability
When comparing a platform analysis to an **existing** experiment analysis that was run in the past, the underlying data may have fallen out of retention or been deleted. To check this, compare the table's retention policy to the analysis dates used in your original experiment analysis to confirm the data still exists.
## Verify that results match
After completing the above four steps, your offline analysis should produce results that match those in the Statsig console.
{% callout type="note" %}
The Statsig Console includes several advanced features, such as [winsorization](/experiments/statistical-methods/methodologies/winsorization#winsorization-statsig-whn), [CUPED](/experiments/statistical-methods/variance-reduction#cuped), and [the delta method](/experiments/statistical-methods/variance#ratio-and-mean-metrics) to address ratio metrics. Disable these features initially when comparing results.
{% /callout %}
[This article](https://www.statsig.com/blog/how-to-analyze-an-experiment-from-databricks-tables) provides an example of conducting offline calculations in Databricks.
If you have additional questions, send Statsig a Slack message.
================================================================================
# Warehouse Native Debugging Guide
URL: https://docs.statsig.com/statsig-warehouse-native/guides/debugging
Updated: 2026-07-11
================================================================================
## Common debugging scenarios
When interacting with complex data sources, you may encounter unexpected results. Statsig proactively monitors for these and notifies you if something looks off. This guide helps you identify what went wrong and reconcile differences in results between Statsig's analysis and your own.
## Common issues that cause errors or missing data
### Mismatched IDs
In many cases, you may have multiple versions of the same ID across tables. For example, the user `abc123` in one source might be `USER_abc123` in another, or certain loggers may hash ID values for privacy reasons.
This usually triggers a Statsig alert that it was unable to join data between sources. To resolve this:
* Go to the relevant sources and run the sample queries to check for obvious ID mismatches.
* If no mismatch is obvious, find the job that is unable to join sources and copy the SQL. Working through the SQL query should pinpoint where the mismatch is occurring.
### Conflicting filters
Statsig allows a high degree of customization in explore queries and on explore pages. This can lead to scenarios where two conflicting filters are added that, together, never pass. For example:
* You have a metric with a cohort window from 7 to 13 days, but set up your experiment analysis to run on users' first 6 days. These two filters return no users.
* You create a count metric filtered to `event='purchase'`, and then create a local metric that filters to `event='checkout'`. This set of filters also returns a null set.
To resolve conflicting filters, go to the Unit-Level Aggregations jobs (where filters are rendered) and search for the metric name of interest in the SQL code.
## Common points of confusion
Many data scientists run their own analyses using the Statsig staging data in their warehouse. In some cases, they see very different results. A few common methodology differences tend to explain these discrepancies:
### Join conditions
Statsig joins events and metrics to exposures based on the event or metric input data occurring after the user was exposed to the experiment being analyzed. If you enable the Treat Timestamp as Date setting, the join is done by comparing dates. By default, Statsig uses a timestamp. Not having a time filter, or using a date when Statsig uses a timestamp (or vice versa), can yield very different results.
### Winsorization/capping
You can configure metrics to be capped or winsorized in Statsig. If you have extreme outliers, you'll see significant differences in results if you don't apply this procedure. You can apply winsorization yourself, or create a local metric (or clone the metric) without winsorization.
### CUPED
CUPED can greatly reduce variance, and for aggregation (non-ratio) metrics can shift the group-level mean estimates if there is pre-experiment bias. You can disable CUPED in the scorecard at any time to view the non-CUPED results for comparison.
### Metric types
In some cases, users have assumed that their input data is binomial, meaning the metric is only 1 or 0 at a unit/user level. If this isn't the case and the metric is configured as a count or sum, the result can be much higher variance than expected.
================================================================================
# View SQL
URL: https://docs.statsig.com/statsig-warehouse-native/guides/sql
Updated: 2026-07-11
================================================================================
Statsig Warehouse Native runs SQL in your warehouse to generate experiment results. The queries are fully transparent and visible in the console, so you can trace any result back to its calculation, artifacts, and raw events.
The [pipeline-overview](/statsig-warehouse-native/analysis-tools/pipeline-overview) describes how queries are orchestrated to produce experiment results. To debug or inspect details, find the experiment-specific SQL by going to the experiment’s Pulse view, then selecting view history, then view details on a specific pulse load. That page shows all jobs that contributed to that pulse load.
{% figure %}

{% /figure %}
A few queries with a \[Health Check] prefix automatically check the health of the experiment. Go to [Monitor an Experiment](/statsig-warehouse-native/features/monitor-an-experiment) to learn what these queries do.
For experiment result calculation queries, below are one-sentence summaries of what each query does.
* First exposure: Generate the first exposure time of this experiment at the unit level, given the assignment source.
* Exposure summary: Summarize the first exposures at the group level and generate cumulative exposures.
* User-level calculations: Calculate unit-level metrics at unit \_ metric \_ day grain.
* Windowed rollups: Rollup unit-level metrics at different time windows.
* Pluse calculation: Generate the sample parameters that are necessary for calculating treatment effects, such as units, total, mean, standard deviation, population variance, covariance, etc.
================================================================================
# Bootstrapping Your Experimentation Program
URL: https://docs.statsig.com/statsig-warehouse-native/guides/experimentation-program
Updated: 2026-07-11
================================================================================
This guide outlines a proven approach to starting an experimentation program using Statsig at your company. You can get started with Statsig’s free tier, which includes up to 5M events per month and no limit on the number of team members.
## 1. Generate product ideas
Generating ideas for experimentation is essential to running a successful program. Common methods include dogfooding your product, studying competitors, analyzing existing metrics, and gathering UX research and user feedback.
Here are three common frameworks used at Statsig and among customers:
### Generate ideas based on your business context
1. **Turn every upcoming feature into an experiment**:
* At Statsig, new features are placed behind feature flags. Statsig’s [Feature Flags](/feature-flags/overview) (also called "gates") automatically convert a feature rollout into an A/B test, measuring the impact on key metrics as the rollout progresses. If your engineering team already uses feature flags for releases, this is the most direct path to your first A/B test.
2. **Work backwards from company goals**:
* Many customers begin by aligning experiments with strategic company goals. For example, if the goal is to drive adoption growth, break down the goal into actionable metrics (e.g., total revenue, monthly active users) and run experiments that can move those metrics incrementally. Instead of targeting broad goals like revenue, focus on specific, actionable metrics like checkout completions or user onboarding time.
3. **Break down your business levers**:
* Decompose key drivers of your business to reveal more granular areas where you can experiment. For example, an e-commerce business might focus on increasing the checkout rate, which could lead to experiments on improving the CTA, reducing friction, or building urgency.
{% figure %}

{% /figure %}
### Prioritize your ideas
Use a simple return-on-investment (RoI) approach to prioritize your experimentation ideas:
* Choose ideas that are **simple to execute**, **easy to measure**, and **high in potential**.
* Start with ideas that are easy to implement. After running a few experiments, focus on higher-impact or more controversial ones.
Generating a prioritized list of ideas accounts for more than half the work in running a successful experimentation program.
{% callout type="tip" %}
**(Optional) Create a Backlog**
You can create an experimentation backlog from your list of ideas. Because running experiments often generates more ideas, treat the backlog as optional and useful for future planning.
{% /callout %}
## 2. Run a simple A/B test
### Clarify your hypothesis
Start by formulating a hypothesis for one of your high-priority ideas. A well-framed hypothesis defines the expected outcomes of your experiment, which makes it easier to align with stakeholders and evaluate results objectively.
A hypothesis consists of three key elements: **Action**, **Predicted Outcome**, and **Rationale**. Example:
| Element | Example |
| --------------------| ---------------------------------------------------------------------------------- |
| **Action** | If we reduce the number of fields on the sign-up page |
| **Predicted Outcome**| Then the percentage of users that complete sign-up will increase |
| **Rationale** | Because users will spend less time and effort, leading to higher completion rates |
### Create your experiment
To create an A/B test, go to the [Statsig Console](https://console.statsig.com/login):
1. Name your experiment.
2. Define the variants you want to test.
3. Set the percentage of users eligible to participate.
### Set up your application for the experiment
To start the experiment:
1. Use a Statsig SDK to assign users to experiment variants.
2. Validate assignments through the *Diagnostics* tab in the Statsig console. Refer to the [quick start guide](/guides/abn-tests) to set this up.
To compute results, you can:
* Log events directly from your application.
* Pipe events to Statsig through a service like Segment.
* Configure Statsig to ingest data from your warehouse.
### Validate your experiment configuration
After your application is set up:
* Validate the configuration through the Statsig console, which provides a live exposure stream as users encounter different variants.
* Statsig flags issues, such as missing identifiers or mismatches between exposure and application events, so you don’t miss results.
After validation, select **Start** to begin the experiment.
{% callout type="tip" %}
**(Optional) Set a Target End Date**
Set a target end date using Statsig's power calculator. This ensures you make decisions only after the experiment has gathered enough data for reliable results.
{% /callout %}
{% callout type="tip" %}
**Running Experiments in Registration Funnels**
If you don't have user IDs for users who haven't signed up yet, create an experiment using a `stableID`, which Statsig SDKs auto-generate. This acts as the unit of analysis.
{% /callout %}
{% callout type="tip" %}
**Selecting Key Metrics**
When setting up an experiment, configure the key metrics to evaluate your hypothesis. Statsig computes results for all your metrics, but focusing on key metrics helps avoid cherry-picking.
{% /callout %}
### Organize regular experiment reviews
Experiment reviews provide a structured environment to discuss results, share insights, and align on decisions. Run weekly or bi-weekly reviews to reinforce a culture of data-driven decision-making and experimentation rigor.
## 3. Share your results
Sharing your results widely is key to building momentum for experimentation. Here are a few ways to share insights across your organization:
* **Monthly Experimentation Update**: Summarize the number of experiments run, their business impact, and key learnings.
* **Slack #experimentation Channel**: Use a dedicated channel to post real-time experiment learnings and foster discussion.
* **Weekly Experiment Review**: Open these reviews to a broader audience to increase visibility and foster engagement.
## 4. Accelerate your speed of experimentation
### Cement the value of experimentation
As you gain confidence, try balancing ideas with more uncertain outcomes. Counterintuitive results can solidify the value of experimentation and lead to new discoveries.
### Increase experimentation with self-serve experiments
Statsig’s results are trustworthy and easy to interpret, making it possible for any team to run experiments. Reduce the barrier to entry for experimentation by educating team members on how to create and run their own experiments.
### Build incentives to run more experiments
Encourage engineers and teams to prioritize impact over shipping new features. Reward them for experimentation outcomes rather than the volume of shipped features, ensuring a focus on end-user value.
## 5. Build a culture of learning
### Accelerate learning
> “If you double the number of experiments you do per year, you're going to double your inventiveness.”
> Jeff Bezos, Founder of Amazon
Frequent experimentation leads to faster learning, helping teams discover user preferences and make data-driven product decisions. Even inconclusive results can prompt deeper analysis and more informed hypotheses.
### Measure the impact of your experimentation program
Evaluate your program’s success using key criteria such as:
| Success Criteria | Score | Weight |
| ------------------------------------------------------------------- | ----- | ------ |
| Time spent to set up an experiment | | |
| Time spent to prepare experiment results | | |
| Percentage of decisions made using trustworthy data | | |
| Number of experiments run per week | | |
| Quality of experiments run | | |
| Time spent indexing/searching past results and discussions | | |
Use these criteria to score your team's progress, set goals, and demonstrate the productivity boost from a well-implemented experimentation program.
================================================================================
# Forwarded Data
URL: https://docs.statsig.com/statsig-warehouse-native/connecting-your-warehouse/forwarded-data
Updated: 2026-07-11
================================================================================
If you log events or exposures through Statsig's SDK, Statsig forwards that data back to your warehouse in near-real time, on demand.
## Setting up tables for forwarded data
By default, when you set up a data connection, Statsig automatically creates tables named `exposures` and `events` to receive forwarded SDK data.
To rename these tables, go to the data connection page, open the **Advanced** tab, and update the table names.
{% figure %}

{% /figure %}
{% callout type="note" %}
If you've already had data exported and change the table name, Statsig writes future data to the new table.
{% /callout %}
{% callout type="info" %}
Statsig doesn't forward non-production exposures or log events to external warehouses.
{% /callout %}
## Exposures
Logging exposures with Statsig provides real-time diagnostics on the Statsig console and real-time aggregations like exposures by hour.
When you run Pulse analysis, Statsig fast-forwards raw exposures to catch up with the real-time stream. The fast-forward provides all users in your experiment and Pulse results as fresh as approximately 15 minutes, assuming events and metrics arrive at the same speed.
Statsig performs a just-in-time update of exposures in your warehouse when Pulse is loaded, for the first 1 million exposures logged to the experiment. After that threshold, Statsig batches, deduplicates, and writes exposures to your warehouse once a day.
Statsig doesn't deduplicate fast-forwarded exposures, and some fields are missing (notably `user_dimensions`). These fields are included in the subsequent daily load.
Each day, Statsig exports a deduplicated digest to your warehouse to ensure consistency. The digest is deduplicated with the fast-forwarded data as part of the standard Pulse pipeline.
## Duplicate exposure records
Statsig retains exposure data for 30 days and can't deduplicate beyond that window. After 30 days, Statsig re-sends exposures for units first exposed more than 30 days ago that were re-exposed on a given day. Statsig correctly deduplicates these records during analysis in your warehouse. The table doesn't contain unique user records, even after filtering out fast-forwarded exposures, which can also include duplicate records.
{% callout type="info" %}
For gates with 0% or 100% rollout, Statsig doesn't forward exposure to your warehouse by default. If you need them, contact the support team, your sales contact, or the [Slack community](https://statsig.com/slack).
{% /callout %}
## Events
If you use Statsig SDKs to capture custom events, Statsig exports those events to your warehouse hourly. Pulse results on metrics derived from those events are as fresh as approximately 1 hour.
================================================================================
# Connect Your Warehouse
URL: https://docs.statsig.com/statsig-warehouse-native/guides/connect
Updated: 2026-07-11
================================================================================
{% callout type="note" %}
Warehouse Native is part of Statsig's Enterprise tier. [Contact us](https://statsig.com/contact/demo) to get started.
{% /callout %}
To run analysis on your warehouse, Statsig needs to connect to your warehouse through a service user. You control the access this user gets to your warehouse. In general, Statsig requires:
* Read access to metric data and exposures data
* Write access to an isolated Statsig Staging database for writing results and exporting data
* Access to run jobs and queries
## Choose your warehouse
The following warehouses/tools are supported in Statsig Warehouse Native:
* [Snowflake](/statsig-warehouse-native/connecting-your-warehouse/snowflake)
* [Athena](/statsig-warehouse-native/connecting-your-warehouse/athena)
* [Bigquery](/statsig-warehouse-native/connecting-your-warehouse/bigquery)
* [Databricks](/statsig-warehouse-native/connecting-your-warehouse/databricks)
* [Redshift](/statsig-warehouse-native/connecting-your-warehouse/redshift)
================================================================================
# Snowflake Connection
URL: https://docs.statsig.com/statsig-warehouse-native/connecting-your-warehouse/snowflake
Updated: 2026-07-11
================================================================================
## How Snowflake connection works
To set up a Warehouse Native connection with Snowflake, Statsig needs the following:
* Account Name
* Database Name
* Schema Name
* Service User Name
* If authenticating using login credentials:
* Service User Password
* If authenticating using key-pair authentication:
* Private Key
* Private Key Passphrase (Optional)
The service user needs the following permissions:
* READ on any tables you are using for experimentation
* USAGE/WRITE on a Statsig-specific schema that Statsig uses to materialize temp tables and results
{% callout type="info" %}
If your data warehouse is IP protected, you must include allowlisting of Statsig IP ranges in your setup steps.
{% /callout %}
### Account name
For the Account Name field, enter in the format `..`. For example:
`xy12345.us-central1.gcp`
To get this information, navigate to the bottom left of your Snowflake console, as shown below, and copy the link URL:
{% figure %}

{% /figure %}
The copied URL will look something like this:
`https://xy12345.us-central1.gcp.snowflakecomputing.com`
Extract the required Account Name value from this URL. For this example, the Account Name is `xy12345.us-central1.gcp`.
{% callout type="info" %}
### Using \-\ for account name
For the Account Name field, you can also enter your Snowflake [account identifier](https://docs.snowflake.com/en/user-guide/admin-account-identifier.html#format-1-preferred-account-name-in-your-organization), which typically takes the form `-`. To find the `` in the Snowflake console, click on your account profile (usually at the bottom left) to view account details as shown below.
{% figure %}

{% /figure %}
{% /callout %}
### Database and schema name
Provide the Schema and corresponding Database where Statsig will materialize results.
{% figure %}

{% /figure %}
### Key-pair authentication
To set up key-pair authentication, follow the [Snowflake documentation](https://docs.snowflake.com/en/user-guide/key-pair-auth) to generate the private and public keys, then set the public key on the service user.
Provide the private key in the field below:
{% figure %}

{% /figure %}
### Boilerplate setup SQL
To create a Statsig user with sufficient privileges and a Statsig staging schema, run the following code in a Snowflake worksheet with sysadmin and securityadmin roles.
Replace `` and `` with your values, which you copy into the Statsig console.
Adjust the warehouse size based on the scope of your data.
```sql expandable
BEGIN;
-- set up variable values to be used in statements later
-- make sure to configure user_name and user_password with your own values
SET user_name = ''; -- REPLACE WITH YOUR OWN VALUE
SET user_password = ''; -- REPLACE WITH YOUR OWN VALUE
SET role_name = 'STATSIG_ROLE';
-- change role to sysadmin for warehouse / database steps
USE ROLE sysadmin;
-- create a warehouse, database, schema and tables for Statsig
CREATE OR REPLACE WAREHOUSE STATSIG WITH warehouse_size='XLARGE'; -- adjust based on your data size
CREATE DATABASE IF NOT EXISTS STATSIG_STAGING;
CREATE SCHEMA IF NOT EXISTS STATSIG_STAGING.STATSIG_TABLES;
-- change current role to securityadmin to create role and user for Statsig's access
USE ROLE securityadmin;
-- create role for Statsig
CREATE ROLE IF NOT EXISTS identifier($role_name);
GRANT ROLE identifier($role_name) TO ROLE SYSADMIN;
-- create a user for Statsig
CREATE USER IF NOT EXISTS identifier($user_name)
password = $user_password
default_role = $role_name
default_namespace = STATSIG_STAGING.STATSIG_TABLES
default_warehouse = STATSIG;
GRANT ROLE identifier($role_name) TO USER identifier($user_name);
-- grant Statsig role access to create warehouse and schema
GRANT USAGE ON WAREHOUSE STATSIG TO ROLE identifier($role_name);
GRANT USAGE ON SCHEMA STATSIG_STAGING.STATSIG_TABLES TO ROLE identifier($role_name);
GRANT CREATE SCHEMA, MONITOR, USAGE ON DATABASE STATSIG_STAGING TO ROLE identifier($role_name);
-- ONLY GIVE THIS LEVEL OF ACCESS in the staging schema.
GRANT CREATE TABLE, CREATE FUNCTION ON SCHEMA STATSIG_STAGING.STATSIG_TABLES TO ROLE identifier($role_name);
GRANT SELECT, UPDATE, INSERT, DELETE ON ALL TABLES IN SCHEMA STATSIG_STAGING.STATSIG_TABLES TO ROLE identifier($role_name);
GRANT SELECT, UPDATE, INSERT, DELETE ON FUTURE TABLES IN SCHEMA STATSIG_STAGING.STATSIG_TABLES TO ROLE identifier($role_name);
GRANT OWNERSHIP ON FUTURE TABLES IN SCHEMA STATSIG_STAGING.STATSIG_TABLES TO ROLE identifier($role_name);
-- grant Statsig role read access to database and schema passed in
-- do this at a table level, database level, and/or schema level
-- for data Statsig needs to access
GRANT USAGE ON DATABASE TO ROLE identifier($role_name);
GRANT USAGE ON SCHEMA . TO ROLE identifier($role_name);
GRANT SELECT ON ALL TABLES IN DATABASE TO ROLE identifier($role_name);
GRANT SELECT ON FUTURE TABLES IN DATABASE TO ROLE identifier($role_name);
GRANT SELECT ON ALL VIEWS IN DATABASE TO ROLE identifier($role_name);
GRANT SELECT ON FUTURE VIEWS IN DATABASE TO ROLE identifier($role_name);
COMMIT;
```
### What IP addresses will Statsig access data warehouses from
[Go to FAQ](/data-warehouse-ingestion/faq#what-ip-addresses-will-statsig-access-data-warehouses-from)
### Additional setup for Warehouse Explorer
Warehouse Explorer lets you find and bring data from any table into Statsig for ad-hoc analysis.
To enable Warehouse Explorer, grant Statsig additional permission to query the INFORMATION\_SCHEMA metadata tables. These tables allow the Statsig user to read schema, column, and table definitions.
```sql
GRANT USAGE ON SCHEMA .INFORMATION_SCHEMA TO ROLE identifier($role_name);
```
================================================================================
# Athena Connection
URL: https://docs.statsig.com/statsig-warehouse-native/connecting-your-warehouse/athena
Updated: 2026-07-11
================================================================================
## Athena Warehouse Native overview
To set up a connection with Athena, Statsig needs the following:
* An S3 Bucket
* A Glue Database for staging
* An S3 Query Result Location
* Athena Access Permissions (using an AWS Role or an AWS User)
## Set up Statsig staging structure
1. Create or choose an S3 Bucket. Statsig uses a subfolder inside this S3 Bucket to store all staging data. Statsig has write-access to ONLY this scoped subfolder of this S3 Bucket (specifically labeled `Statsig S3 Folder` in your Data Connection settings in the Statsig Console).
2. Create or choose a Glue Database (can be `default`). Statsig uses this as a staging Database to create and manage tables. Statsig can drop/create tables within ONLY this staging Database.
3. Choose an S3 Query Result Location folder within the S3 Bucket. This S3 location acts as the Output Location for `SELECT` queries run in your Athena Warehouse. You can specify this location either:
* Explicitly as an S3 location (ex: `s3://my_bucket/my_query_results_folder/`)
* OR as part of a setting within an Athena Workgroup
* NOTE that your workgroup must have the 'Query result location' field populated accordingly
4. Add this information, along with your AWS Region, to your Data Connection settings in the Statsig Console.
## Grant permissions to Statsig
Grant the following permissions for Statsig from your AWS console:
* READ on any tables and data you are using for experimentation
* USAGE and WRITE on a Statsig-specific schema used to materialize temp tables and results. This enables caching and incremental loads. You specify which Glue Database and S3 Bucket to use, and Statsig creates a Statsig S3 Subfolder for staging operations.
1. Create an AWS IAM Policy to house the required access permissions
This policy contains the permissions required for Statsig to access your warehouse. You can return to this policy later and edit it as needed.
* In your AWS IAM Dashboard, select the Policies page under the Access Management tab
* Click 'Create policy'
* Switch the Policy Editor type from 'Visual' to 'JSON'
* Copy and paste the below JSON template block
* Replace the placeholders with your setup information and the Statsig S3 Folder (specified in your Statsig project's Data Connection settings)
* Specify all S3 locations and Glue Databases of any read-only assignment/metric data
* Remove the descriptor comments
```json expandable
{
"Version": "2012-10-17",
"Statement": [
// Allow Statsig to recognize your staging S3 Bucket
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::__S3_BUCKET__"
},
// Allow Statsig to read events/exposures data from your S3 Buckets
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::__PATH_TO_YOUR_READONLY_DATA__/*"
},
// Allow Statsig to read events/exposures tables from your Glue Databases
{
"Effect": "Allow",
"Action": [
"glue:GetTable",
"glue:GetTables",
"glue:GetDatabase",
"glue:GetDatabases",
"glue:GetPartition",
"glue:GetPartitions"
],
"Resource": [
"arn:aws:glue:__REGION__:__YOUR_AWS_ACCOUNT_ID__:database/__YOUR_READONLY_DATABASE__",
"arn:aws:glue:__REGION__:__YOUR_AWS_ACCOUNT_ID__:table/__YOUR_READONLY_DATABASE__/*"
]
},
// Allow Statsig to read/write/use data and tables in an isolated staging S3 subfolder
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"athena:StartQueryExecution",
"athena:GetQueryResults",
"athena:GetQueryExecution",
"athena:StopQueryExecution",
"glue:GetTable",
"glue:GetTables",
"glue:CreateTable",
"glue:UpdateTable",
"glue:DeleteTable",
"glue:GetPartition",
"glue:GetPartitions",
"glue:CreatePartition",
"glue:UpdatePartition",
"glue:DeletePartition",
"glue:BatchCreatePartition",
"glue:BatchDeletePartition",
"glue:GetDatabase",
"glue:GetDatabases"
],
"Resource": [
"arn:aws:s3:::__S3_BUCKET__/__PATH_TO_S3_QUERY_RESULTS_FOLDER__/*",
"arn:aws:s3:::__S3_BUCKET__/__STATSIG_S3_FOLDER__/*",
"arn:aws:athena:__REGION__:__YOUR_AWS_ACCOUNT_ID__:workgroup/__WORKGROUP_NAME__",
"arn:aws:glue:__REGION__:__YOUR_AWS_ACCOUNT_ID__:catalog",
"arn:aws:glue:__REGION__:__YOUR_AWS_ACCOUNT_ID__:database/__GLUE_STAGING_DATABASE__",
"arn:aws:glue:__REGION__:__YOUR_AWS_ACCOUNT_ID__:table/__GLUE_STAGING_DATABASE__/*"
]
}
]
}
```
2. Create an IAM Role or IAM User:
{% tabs %}
{% tab title="IAM Role" %}
With an IAM Role, Statsig assumes your IAM Role through a Statsig Service Account. The Statsig Account ID for this service account is in your Data Connection settings in the Statsig Console. Statsig runs queries on behalf of this IAM Role.
Optionally, you can add an External ID condition for added security ([AWS External ID Docs](https://aws.amazon.com/blogs/security/how-to-use-external-id-when-granting-access-to-your-aws-resources/)). Statsig generates this External ID, which you can view in your Data Connection settings in the Statsig Console.
* In your AWS IAM Dashboard, select the Roles page under the Access Management tab
* Click 'Create role'
* Choose 'AWS account' as the Trusted entity type
* Choose 'Another AWS account' from the options, and copy the Statsig Account ID from your Statsig console
* Optionally, require use of an External ID for connections to this role (also specified in your Statsig console)
* Continue to next step of setup, and select your IAM Permissions Policy from earlier
* Name, review, and create; your Trust Policy JSON should follow the format below:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Principal": {
"AWS": "__STATSIG_ACCOUNT_ID__"
},
"Condition": {
"StringEquals": {
"sts:ExternalId": "__ROLE_EXTERNAL_ID__"
}
}
}
]
}
```
* Add the ARN for this IAM Role to your Data Connection settings in the Statsig Console
{% /tab %}
{% tab title="IAM User" %}
With an IAM User, Statsig uses AWS Access Keys to access the IAM User and runs queries on behalf of that user.
* In your AWS IAM Dashboard, select the Users page under the Access Management tab
* Click 'Create user'
* Name your user
* On the next step of setup, choose 'Attach policies directly' and select your newly created Permissions Policy
* Under the Security Credentials tab of this newly created User, find the Access Keys block
* Click 'Create access key', and choose 'Third-party service' from the Use Case options
* Add the Access Key and Secret Access Key to your Data Connection settings in the Statsig Console
{% /tab %}
{% /tabs %}
## Set up reading data from your events/exposures tables
1. Give Statsig read-access to your Glue Database containing any tables you need Statsig to read from. Do this by adding the following to your AWS IAM Permissions Policy:
```
{
"Effect": "Allow",
"Action": [
"glue:GetTable",
"glue:GetTables",
"glue:GetDatabase",
"glue:GetDatabases",
"glue:GetPartition",
"glue:GetPartitions"
],
"Resource": [
"arn:aws:glue:__REGION__:__YOUR_AWS_ACCOUNT_ID__:database/__YOUR_READONLY_DATABASE__",
"arn:aws:glue:__REGION__:__YOUR_AWS_ACCOUNT_ID__:table/__YOUR_READONLY_DATABASE__/*"
]
}
```
2. Give Statsig read-access to your S3 Bucket locations of the tables you need Statsig to read from. Add this to your AWS IAM Permissions Policy:
```
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::__PATH_TO_YOUR_READONLY_DATA__/*"
}
```
3. In Statsig, when setting up Metric or Assignment Sources, select from these tables using `"database"."table"` format.
4. Repeat for any additional tables, or when you need to read a new table from Statsig.
{% callout type="info" %}
If your data warehouse is IP protected, add Statsig IP ranges to your allow list as part of your setup steps.
{% /callout %}
## Additional Athena resources
### S3 bucket encryption guide
Statsig supports encryption for all accessed S3 Buckets. To allow Statsig to access encrypted S3 Buckets:
1. From the AWS Key Management Service console, create a new KMS Key using the below cryptographic configuration settings: ([AWS SSE KMS Docs](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html?icmpid=docs_s3_hp_batch_ops_create_job_encryption_key_type))
* Key Type: Symmetric
* Key Usage: Encrypt and Decrypt
* Advanced Options
* Key Material Origin = KMS
* Regionality = Single-Region Key
2. From the Key Policy tab of your newly created KMS Key, find the Key Administrators box. Click Add, and select the AWS IAM Role/User you provided to Statsig as an administrator.
3. Navigate to your S3 Bucket. From your S3 Bucket Properties tab, find the Default Encryption box. Click Edit, and select the below default encryption settings:
* Encryption Type: SSE-KMS
* AWS KMS Key: Enter AWS KMS Key ARN
* (enter your newly created KMS Key ARN in the box)
* Bucket Key: Enable
### Statsig staging architecture details
Statsig creates all staging tables within the Glue Staging Database that you create and provide. Statsig creates all tables as table-type `ICEBERG`, except the forwarded exposures and events tables, which are regular `EXTERNAL` Athena tables.
Statsig stores all staging data within the `Statsig S3 Folder` of your S3 Bucket. Statsig manages the structure within this subfolder. The folder structure is as follows:
* S3 Bucket (you create and provide Statsig the name)
* Statsig S3 Folder (Statsig creates and names, name provided in the Data Connection settings in your Statsig Console)
* Experiment Folder(s) (Statsig creates a subfolder for each unique experiment you run, named `experiment-`)
* Staging Tables Folders (Statsig puts data for each created staging table in its own subfolder, named intuitively after the data they contain)
* `metadata/`
* Iceberg Table Metadata
* `data/`
* Table Data Files (stored as Parquet)
* `statsig_forwarded_exposures/`
* Forwarded Exposures Table Subfolder (named in the Data Connection settings in your Statsig Console)
* Dates in `YYYY-MM-DD` format (This table is partitioned by date, and there is a subfolder for each date that has exposure data sent through Statsig)
* Forwarded Exposure Data
* `statsig_forwarded_events/`
* Forwarded Events Table Subfolder (named in the Data Connection settings in your Statsig Console)
* Dates in `YYYY-MM-DD` format
* Forwarded Event Data
### What IP addresses does Statsig use to access data warehouses?
Go to the [FAQ](/data-warehouse-ingestion/faq#what-ip-addresses-will-statsig-access-data-warehouses-from) for the list of IP addresses.
================================================================================
# Bigquery Connection
URL: https://docs.statsig.com/statsig-warehouse-native/connecting-your-warehouse/bigquery
Updated: 2026-07-11
================================================================================
## How BigQuery connection works
To set up a connection with BigQuery, there are two supported methods:
* Grant Permissions to a Statsig-owned Service Account
* Provide Credentials for Your Own First-Party Service Account
In both cases, you need:
* Your BigQuery Project ID
* The dataset Statsig uses to save temporary tables and materialized results
After you choose your method, start by enabling the BigQuery source in your project settings.
## Grant permissions to Statsig's service account
Grant Statsig access to your BigQuery data from your Google Cloud console.
1. In your BigQuery's [IAM & Admin settings](https://console.cloud.google.com/iam-admin/), add the Statsig service account you copied in the Statsig Console as a new principal for your project and give it the following roles:
* `BigQuery User`
{% figure %}

{% /figure %}
2. Navigate to your [BigQuery SQL Workspace](https://console.cloud.google.com/bigquery), choose the dataset, click **+ Sharing** > **Permissions** > **Add Principal**, and give the same Statsig service account the "BigQuery Data Viewer" role. Repeat for any datasets you want the service user to access.
{% figure %}

{% /figure %}
3. Repeat step 2 to give the "BigQuery Data Editor" role on the dataset Statsig uses for staging data.
{% callout type="info" %}
If your data warehouse is IP protected, you must include allowlisting of Statsig IP ranges in your setup steps.
{% /callout %}
The service account has the required permissions to run queries and materialize results.
## Using a first-party service account
{% callout type="warning" %}
This method isn't recommended. Using service accounts is the [preferred option for enhanced security](https://docs.cloud.google.com/iam/docs/migrate-from-service-account-keys).
{% /callout %}
1. On the BQ service accounts page, click 'Manage Keys' for the service account you want to use
{% figure %}

{% /figure %}
2. Create a new JSON key (which will download a JSON file)
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
3. In the **Statsig Settings** -> [Data Connection](https://console.statsig.com/data_connection/connection_setup) tab, paste the above key into the **Service Account Private Key** field under the **Advanced** toggle.
## Additional BigQuery resources
### BigQuery project ID
Find your BigQuery Project ID:
1. Click on your Project Dropdown inside your Cloud Console.
{% figure %}

{% /figure %}
2. Copy and paste relevant Project ID from the modal pop-up.
{% figure %}

{% /figure %}
{% callout type="info" %}
If your data warehouse is IP protected, you must include allowlisting of Statsig IP ranges in your setup steps.
{% /callout %}
### Statsig Customer ID on BigQuery
C01d5f80s
### IP addresses Statsig accesses data warehouses from
[Go to FAQ](/data-warehouse-ingestion/faq#what-ip-addresses-will-statsig-access-data-warehouses-from)
### Additional setup for Warehouse Explorer
Warehouse Explorer lets you find and bring data from any table into Statsig for ad-hoc analysis.
Statsig reads from `INFORMATION_SCHEMA.COLUMNS`, which requires explicit permission to view table metadata. Grant access by repeating steps 1 and 2 from the [Grant Permissions](/statsig-warehouse-native/connecting-your-warehouse/bigquery#grant-permissions-to-statsig%E2%80%99s-service-account) section for the "BigQuery Metadata Viewer" role.
================================================================================
# Databricks Connection
URL: https://docs.statsig.com/statsig-warehouse-native/connecting-your-warehouse/databricks
Updated: 2026-07-11
================================================================================
## How Databricks connection works
To set up a connection with Databricks, you need the following:
* Your Databricks Server Hostname
* An HTTP Path to a Cluster or SQL Warehouse
* A staging database for writing results and intermediate tables into
* An access token with read access on your experiment data and write access to the staging database
* Use either Serverless SQL Warehouse or an always-on cluster. Statsig uses interactive queries during setup (and some analysis). These queries fail if the cluster takes several minutes to start.
Start by enabling the Databricks source in your project settings.
## Databricks access considerations
For users whose primary warehouse is a DBFS-based Delta Lake, Databricks permissions are simple to manage. For customers using Databricks as an intermediary to other data sources, ensure that Databricks and the Statsig service user have appropriate access to your storage (for example, S3 for Athena tables). Permissions can be reset over time, which causes errors. To validate permissions, try creating tables through the Statsig console.
## Getting connection information
1. Follow the [Databricks documentation](https://docs.databricks.com/integrations/jdbc-odbc-bi.html#get-connection-details-for-a-cluster) to get the hostname and HTTP path of the cluster you use to run your experimental analysis. You may want to create a dedicated cluster for this purpose.
{% figure %}

{% /figure %}
2. Follow the [personal access token instructions](https://docs.databricks.com/dev-tools/auth.html#databricks-personal-access-tokens) to get the token used to calculate experiment results on your warehouse. Alternatively, follow the [service principal instructions](https://docs.databricks.com/en/administration-guide/users-groups/service-principals.html#manage-personal-access-tokens-for-a-service-principal) to get a token for a service principal.
{% figure %}

{% /figure %}
3. Create or choose a database to use. For example, you could run this sql in a notebook:
```sql
staging_database_name = ''
spark.sql(f"CREATE DATABASE IF NOT EXISTS {staging_database_name}")
```
{% callout type="info" %}
If your data warehouse is IP protected, you must include allowlisting of Statsig IP ranges in your setup steps.
{% /callout %}
### IP addresses Statsig accesses data warehouses from
[Go to FAQ](/data-warehouse-ingestion/faq#what-ip-addresses-will-statsig-access-data-warehouses-from)
### Additional setup for Warehouse Explorer
Warehouse Explorer lets you find and bring data from any table into Statsig for ad-hoc analysis.
When Unity Catalog is enabled, Statsig reads from `.information_schema.columns`. You can provide the catalog name in Data Connection settings, under the Advanced section.
You may also need to provide read access to the catalog and the `information_schema` schema:
```sql
GRANT USE CATALOG ON CATALOG TO ;
GRANT USE SCHEMA ON SCHEMA .information_schema TO ;
```
`` is the user associated with the access token you provided to Statsig.
================================================================================
# Redshift Connection
URL: https://docs.statsig.com/statsig-warehouse-native/connecting-your-warehouse/redshift
Updated: 2026-07-11
================================================================================
## How Redshift connection works
To set up a connection with Redshift, Statsig needs the following information:
* Cluster Endpoint
* A service user Username
* A service user Password
* A staging schema that Statsig can write results to
{% callout type="note" %}
SHA256 passwords aren't supported. Use MD5 to avoid issues.
{% /callout %}
Find this information in your AWS console within your specific cluster, as shown in the image below. The service user must be able to read the required experiment data and write to the Statsig staging schema you specify.
{% figure %}

{% /figure %}
When you save the connection, Statsig runs a series of test commands to validate permissions: for example, creating a temp table, running a select/delete statement on that table, and dropping the table.
{% callout type="note" %}
The provided Service Account will require the following attributes:
`enable_case_sensitive_identifier`, `enable_case_sensitive_super_attribute`.
If these aren't already set, Statsig sets them to TRUE upon setup completion.
{% /callout %}
## SSH tunneling
For Redshift connections, Statsig supports SSH tunneling into your Redshift cluster for more secure and private database access.
To enable access, Statsig requires:
* SSH Host
* SSH Port
* SSH User
Statsig uses this information to generate an SSH key. Add this generated key to your `~/.ssh/authorized_keys` file on your SSH proxy machine to enable SSH tunneling.
### What IP addresses will Statsig access data warehouses from
{% callout type="info" %}
If your data warehouse is IP protected, you must include allowlisting of Statsig IP ranges in your setup steps.
{% /callout %}
[Go to FAQ](/data-warehouse-ingestion/faq#what-ip-addresses-will-statsig-access-data-warehouses-from)
### Additional setup for Warehouse Explorer
Warehouse Explorer lets you find and bring data from any table into Statsig for ad-hoc analysis.
To enable Warehouse Explorer, you may need to grant Statsig additional permission to query the `pg_table_def` metadata. Query results include only schemas that Statsig has read access to. A superuser or admin can grant access to additional relevant schemas and table metadata in `pg_table_def` by running:
```sql
GRANT USAGE ON SCHEMA TO ;
GRANT SELECT ON ALL TABLES IN SCHEMA TO ;
```
`` is the service user username you provided to Statsig. `` is the name of the schema to include in Warehouse Explorer results.
================================================================================
# Other Warehouses
URL: https://docs.statsig.com/statsig-warehouse-native/connecting-your-warehouse/other
Updated: 2026-07-11
================================================================================
## Other warehouses
Statsig's architecture supports adding new warehouses. Statsig is selective about which warehouses to support long-term, but is open to discussing your needs and building a solution for your cloud provider.
Statsig accesses your warehouse through API calls and requires the address of your cluster and authentication information, similar to running queries through DataGrip, Python, or other tools.
## Permissions
Statsig requires read permissions on any input tables for your analysis. Statsig also requires full CRUD (create/read/update/delete) permissions on a "sandbox" where staging data and intermediate data artifacts are stored as part of experimentation pipelines.
### What IP addresses will Statsig access data warehouses from
{% callout type="info" %}
If your data warehouse is IP protected, you must include allowlisting of Statsig IP ranges in your setup steps.
{% /callout %}
[Go to FAQ](/data-warehouse-ingestion/faq#what-ip-addresses-will-statsig-access-data-warehouses-from)
================================================================================
# Egress, Privacy, & Storage
URL: https://docs.statsig.com/statsig-warehouse-native/analysis-tools/data-privacy
Updated: 2026-07-11
================================================================================
One advantage of using Statsig Warehouse Native is that user-level data comes directly from your source of truth without being copied or leaving your warehouse. This page walks through how Statsig interacts with your warehouse.
## Permissions
The permissions Statsig requires are:
* Job Access (where applicable) to run queries as a service user
* Read Access on event, metric, and exposure data you want to use for experiment analysis
* Statsig only selects from these tables
* Read, Write, Delete access in a Statsig Staging environment you specify
* In Bigquery this is a dataset
* In Snowflake and Redshift this is a schema
* In Databricks this is a database, or a database within a separate workspace with a scoped delta share to your production datasets
Best practice is to create a new dataset for Statsig Staging to make sure this environment is isolated. Statsig only modifies tables it creates as part of analysis.
## What Statsig reads
Statsig only reads two forms of data, both of which are very small (generally in Kilobytes)
* Small samples used for validating setup in the following surfaces:
* Metrics Tab
* Metric Source Tab
* Assignment Source Tab
* (If using Statsig exposures) Users Tab
* Aggregated results at the group or experiment level. This data doesn't contain user IDs and is rolled up at the group, metric, group/metric or experiment level. This includes:
* Existing groups and group sizes for experiment setup
* Pulse Results
* Total exposure counts for power analysis
* Daily total metric values
* Experiment-level health checks (e.g. distinct metrics, count of exposures)
## Analysis flow
During analysis, data stays in your warehouse. Intermediate tables and results are written to the Statsig data staging set. When the results of Health Checks or Pulse become available, Statsig consumes those result sets and stores them on its servers as well (usually \< 1000 rows):
{% figure %}

{% /figure %}
## Data retention in Statsig
Statsig retains customer data in exposure events for a maximum of 30 days for diagnostics and debugging, when using the SDK for assignment. Statsig automatically removes this data after 30 days.
## Storage management
Experimentation staging datasets can generate large amounts of data. The data grows with the number of experiments you run. For example, if you have user-day data and run 100 experiments that all expose every user, you get 100 copies of that data with slight differences based on when users were exposed to each experiment.
Statsig includes a table management system to help control storage costs and visibility:
* Temporary artifacts (tables generated as part of explore queries or pulse results) are dropped 2-7 days after creation. This gives some buffer to debug, but they don't maintain long-term copies of data
* When you finish an experiment by making a decision, Statsig prompts you to delete the tables. By default, Statsig leaves the result sets (on the order of kilobytes) in your warehouse for reference, but you can override this setting.
* At any time, you can drop tables for your experiment from the three-dot menu in the experiment.
================================================================================
# Warehouse Storage
URL: https://docs.statsig.com/statsig-warehouse-native/warehouse-management/storage
Updated: 2026-07-11
================================================================================
## How warehouse storage works
Statsig uses its sandbox in your warehouse to cache intermediate tables and result tables. This enables incremental reloads (without recalculating metrics for every day of the experiment on each load) and allows you to use these tables for ad-hoc analysis.
Statsig stores tables in the sandbox schema or dataset you configured. You can use this to track storage footprint and manage permissions.
## Conventions and usage
Statsig shards tables by entity ID. For example, the experiment `early_user_journey_acceleration` has that identifier in its associated table names for scorecard loads. This is a reliable way to look up tables for a given experiment.
Statsig writes to special tables that appear in metric sources or assignment sources:
* pipeline overview: performance statistics for the jobs Statsig runs
* statsig\_forwarded\_events: events logged through statsig.log\_event
* statsig\_forwarded\_exposures: exposures from experiments, gates, autotunes, and holdouts
* statsig\_forwarded\_switchback\_exposures: switchback-formatted exposures
* statsig\_daily\_results: rendered results with statistics like p-value
Some of these tables have pre-set names; others you configure in data connection settings.
Many users ingest these tables as part of internal pipelines. Configure lookback windows so mutable data doesn't cause issues, because Statsig regularly updates data in these tables and in some cases backfills up to several days when data delays or repairs occur.
{% callout type="note" %}
Exposures don't necessarily deduplicate. Fast-forwarded exposures duplicate records from daily exports, and Statsig retains only 30 days of history for warehouse native projects. After 30 days, Statsig treats a given unit's exposure as new and re-exports it.
{% /callout %}
## Volume
Scorecard loads generate a varying number of tables depending on the number of metric sources accessed and the types of metrics loaded. Statsig may also materialize intermediate tables before or after large operations, which reduces compute cost.
This can produce a large number of artifacts. Customers running 300+ experiments have encountered default quota limits on vendors like Databricks. You can address this by requesting a quota increase or configuring the TTLs described in the Management section.
## Management
Transient tables have a short TTL, usually 1-2 days, and Statsig automatically cleans them up.
Other tables are permanent by default. You can clean them up from the experiment in Statsig's console or as part of launching an experiment. You can also configure TTLs per table type in the data connection section of a project's settings.
Plan to manage storage using your own warehouse tools in addition to Statsig's systems, for example by cleaning up entities that haven't been accessed or modified in the last month. Ideally this isn't necessary given TTLs, but there are known cases where Statsig's internal tracking can consider a table dropped when it still has a storage footprint. Statsig can't guarantee that all tables will be removed.
**How TTLs work**
When Statsig creates or modifies a managed table, it schedules a cleanup at the current time plus the TTL. For example, if a Result table is written on 2024-06-01 and Result tables are configured with a 14-day TTL, a deletion is scheduled for 2024-06-15.
If that table is modified on 2024-06-07 (for example, through a scorecard reload), the deletion request is reset to 2024-06-21, overwriting the existing one. This means incremental updates on long-running experiments keep their staging data until the experiment stops.
Changing TTLs doesn't retroactively affect existing tables' deletion requests. The new TTL applies at the next scorecard load for the relevant experiment.
**Types of tables for TTL**
* `Result Datasets`: the final tables Statsig creates at the end of an experiment or gate reload, containing aggregated group-metric level data. These are generally small (1 row per metric/day/group/dimension) and useful for post-hoc analysis.
* `Intermediate Tables`: all other tables Statsig writes to during an experiment reload. These can be large because they contain user-level data. Statsig reuses them for incremental and metric reloads.
* `Transient Datasets`: tables created for one-off queries (most commonly Explore queries and Power Analyses), or temporary datasets used while creating `Intermediate Tables` as a performance optimization. By default, these are dropped after 2-3 days unless overridden with the setting above.
**Explore query dependencies**: Explore queries rely on permanent staging tables. These tables reduce the need to recompute data for analysis already performed by the scorecard run. Unlike results tables (which are cached locally on Statsig servers), permanent staging tables must be maintained in your warehouse for Explore queries to function. This avoids reprocessing large volumes of data that may contain PII or other sensitive information.
## Troubleshooting storage issues
### Missing data errors
Warehouse Native users may encounter `TABLE_OR_VIEW_NOT_FOUND` errors when required data tables are missing from the warehouse. This typically occurs when:
* **Permanent staging tables have been dropped**: Explore queries and advanced analysis require permanent staging tables, not results or transient staging tables.
* **TTL settings have expired tables**: Tables with configured time-to-live (TTL) settings get automatically cleaned up.
* **Incomplete data loads**: Initial experiment setup or data pipeline issues may prevent table creation.
#### Resolution steps
**For missing staging tables:** missing permanent staging tables require a full reload to recreate the staging dataset.
**For general missing tables:**
1. Check your warehouse's TTL settings in the data connection configuration.
2. Verify that permanent staging tables exist in your configured sandbox schema.
3. If tables were manually dropped, trigger a full data reload.
4. Contact support if tables are still missing after reload, or if you didn't drop them.
#### Storage dependencies
Warehouse Native uses several types of tables with different storage patterns:
* **Permanent staging tables**: required for Explore queries and advanced analysis.
* **Transient staging tables**: short-lived intermediate tables with a mix of automatic cleanup (1-2 days TTL) and permanent storage (small tables useful for ad-hoc analysis like regression coefficients).
* **Results tables**: output statistics from the pipeline, copied and cached locally on Statsig servers.
{% callout type="note" %}
Vacuum jobs don't affect staging tables used by Statsig.
{% /callout %}
================================================================================
# Warehouse Costs
URL: https://docs.statsig.com/statsig-warehouse-native/guides/costs
Updated: 2026-07-11
================================================================================
This page is a high-level summary of how to think about Warehouse Costs and Total Cost of Ownership on Statsig Warehouse Native. There is some overlap with the [best practices](/statsig-warehouse-native/guides/best-practices) page; treat that page as an implementation guide and this page as a high-level overview.
## Why do costs matter?
Warehouse Native platforms require you to provide compute and storage to run data jobs like experiment analyses and analytics queries. This is a cost center, and can meaningfully drive up your warehouse bill if queries are unoptimized or poorly integrated with your databases.
This is a major concern for many customers considering a Warehouse Native solution for Experimentation or Analytics. Warehouse Native platforms are very flexible, but that flexibility can lead to unexpected data costs. Understanding total cost of ownership is important when evaluating a platform, as many platforms don't treat it as a primary focus.
Statsig started as a cloud-only platform with a strong focus on cost efficiency. By continuously optimizing pipelines on the cloud side of its business, Statsig has developed expertise in cost-saving strategies that it passes on to Warehouse Native customers.
## Benchmarks
### Total cost of ownership
Warehouse costs typically hover between 5% and 20% of customers' platform spend with Statsig. There are exceptions depending on usage, but these tend to be on the low end. To avoid cherry picking, the examples below are an anonymized list of the top 5 Statsig Warehouse Native customers on Snowflake, by total warehouse spend:
| Company Profile | Experiments Run, 12 Months | Estimated % Contract Value Spent on Compute |
| ------------------------------------------------------------------------- | -------------------------- | ------------------------------------------- |
| Multi-sided marketplace (e.g. job search / contracts), 100M-1B in Revenue | 120 | 12% |
| Multinational B2C business, 5B-20B in revenue | 210 | 9% |
| B2B SaaS, 100M-1B in Revenue | 250 | 11% |
| Online Entertainment, 100M-1B in Revenue | 180 | 6% |
| Online Services, 50-100M in Revenue | 130 | 20% |
This is fairly representative of what you can expect using Statsig at scale with reasonable adherence to best practices. Costs do scale with number of experiments; companies running fewer experiments generally see a smaller relative TCO.
Relative to experiment size, there is significant variation in spend depending on how many metrics you use, what kind of metrics, and how "dense" your metrics are. The table below shows typical costs when teams follow best practices:
| Experiment Size | Cost per Experiment Load | Lifetime Cost per Experiment (4 Weeks with Daily Refreshes & Drilldowns) | Lifetime Cost Using [Turbo Mode](../features/turbo) |
| ------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------------- |
| Small (50k or less units) | \< $0.05 | $0.25 - $2.00 | $0.25 - $1.00 |
| Medium | \< $0.25 | $1.00 - $10.00 | $1.00 - $5.00 |
| Large | $0.25 - $10.00 | $5.00 - $280.00 | $3.00 - $50.00 |
| Extremely Large (500 Million+ experimental units) | Low Sample with only a handful of companies - Can't Generalize | " | " |
{% callout type="note" %}
[Turbo Mode](../features/turbo) is especially efficient for large-scale experiments. It dramatically reduces memory usage, keeping large jobs from spilling to disk and reducing the need for XL clusters to provide enough memory.
{% /callout %}
### Industry
Statsig aims to be best-in-class for Warehouse Native experimentation costs. Customers who evaluate multiple vendors consistently report that Statsig is 50-66% of the cost of other platforms.
In cases where Statsig has been more expensive than competitors, the team investigates quickly. For example, a recent customer accidentally used un-partitioned data for their Statsig proof of concept, which drove up cost relative to another vendor where the tables were partitioned. Statsig followed up by building warnings when source data isn't partitioned. After partitioning, cost went from 120% of the other vendor's cost to under 60%.
Multiple customers have migrated to Statsig from other Warehouse Native Experimentation platforms due to cost concerns. Reach out to the Sales team to run an evaluation of relative costs. If you have concerns, or another party claims their solution is more efficient, contact Statsig to discuss.
## Tools for understanding cost
Statsig aims to be as transparent as possible about costs. Numerous tools throughout the product help in this regard:
### History
In every loaded experiment or gate, you can quickly see the cost of each load. The metrics shown are relevant to your warehouse, for example, slot hours or bytes scanned for BigQuery, uptime for Snowflake, and so on.
{% figure %}
{% /figure %}
### Pipeline overview dashboard
Statsig automatically creates a `Pipeline Overview` dashboard in its MEx platform. The underlying data is written to your warehouse, so you can investigate what is driving costs from Statsig. Many customers have built their own monitoring and alerting on top of this dataset and notify Statsig if they see any changes in cost.
{% figure %}
{% /figure %}
### Vendor tooling
Use standalone compute and storage resources for Statsig in your warehouse. This makes total cost of ownership easy to understand. Some cost information isn't visible to the platform without administrator rights on your warehouse.
If there is a mismatch between Statsig's dashboard and your costs, reach out to support. Statsig takes this seriously and works to resolve the gap.
## Tools for managing cost
### Follow UI hints
Statsig proactively flags to you if:
* A data source is particularly slow.
* A metric source is malformed (e.g. a view definition which is broken within your warehouse).
* A data source isn't following best practices.
Paying attention to these and flagging them quickly prevents wasted compute. In most cases where PoC customers had performance issues, they boiled down to issues like their temp tables for the PoC not being partitioned, or not using macros to filter tables with extremely long data retention.
### Access controls
For large organizations, most cost issues are caused by poorly optimized source tables, or using the wrong source when pulling data.
Statsig offers access controls so that:
* Your data team can control data sources and ensure they are well-behaved.
* Verified sources are clearly indicated in the console for end users.
### Following best practices
The [best practices](/statsig-warehouse-native/guides/best-practices) cover most situations that lead to high warehouse costs on Statsig. In short, you should aim to:
* Cluster or partition source tables.
* Use Statsig date macros to guarantee filter push-down.
* Use Statsig's [Turbo Mode](../features/turbo) to reduce redundant compute, especially for large or long experiments.
================================================================================
# Data Best Practices
URL: https://docs.statsig.com/statsig-warehouse-native/guides/best-practices
Updated: 2026-07-11
================================================================================
## Cost management
Statsig's pipelines use many SQL best practices to reduce cost, and smaller customers can run month-long analyses for a few pennies.
Following these best practices helps keep costs under control and consistent.
Go to [Compute Cost Transparency](/statsig-warehouse-native/guides/best-practices#compute-cost-transparency) to understand how much compute time your experiments use.
### Follow SQL best practices
Statsig uses your SQL to connect to your source data. Here's some common issues:
- Avoid using `SELECT *`, and only select the columns you'll need
- `SELECT *` leads to a lack of clarity in what you are pulling/require for other users
- For warehouse like Bigquery and Snowflake, it can increase the scan/size of materialized assets like CTEs in snowflake
- This leads to higher query runtime and higher warehouse bills
- Filter to the data you will need in your base query
- This reduces the scan and amount of data required in future operations
- You can use Statsig Macros (below) to dynamically prune date partitions in subqueries
- Group common metric groups into a single Metric Source
- Statsig filters your source to the minimal set of data and then creates materializations of experiment-tagged data. To maximize the effectiveness of this strategy,
have metric sources that cover common suites of metrics that are usually pulled together.
- Cluster/Partition tables to reduce query scope
- Most warehouses offer some form of partitioning or indexing along frequently filtered keys. In warehouses with clustering, the recommended strategy is:
- Partition/Cluster Assignment Source tables by date, and then your experiment_id column so experiment-level queries can be scoped to that experiment
- Partition/Cluster Metric Source tables based on date, and then the fields you expect to use for filters
These best practices are generally true across all warehouses, especially as the datasets you use for experimentation scale.
### Materialize tables/views
Since Statsig offers a flexible and robust metric creation flow, it's common to write joins or other complex queries in a metric source. As tables and experimental velocity scale, these joins can become expensive because they run across every experiment analysis.
To reduce the impact of this, best practice is to:
- Materialize the results of the join and reference that in Statsig, so you only have to compute the join once
- Use Statsig macros to make sure partitions are pruned before the join, and you only join the data you need
### Use incremental reloads
Statsig offers both Full and Incremental reloads. Incremental reloads process only new data and can be significantly cheaper on long-running experiments. Advanced settings in the Pulse advanced settings section (also available as org-level defaults) help you make tradeoffs to reduce total compute cost. For example, "Only calculate the latest day of experiment results" skips timeseries, but can run large full reloads (for example, 1-year on 100M users) in 5 minutes on a Large Snowflake cluster.
### Reload data ad hoc
Depending on your warehouse and data size, Statsig Pulse results can be available in as little as 45 seconds. Because pipelines have flat costs, reloading 5 days isn't 5 times as expensive as loading one day.
If cost is a concern, choosing which results to schedule versus load on-demand can significantly reduce the amount of processing your warehouse has to do.
### Use Statsig's advanced options
By default, Statsig runs a thorough analysis including historical timeseries and context on your results.
### Use metric-level reloads
Statsig offers Metric-level reloads; this allows you to add a new metric to an experiment and get its entire history, or restate a single metric after its definition has changed. This is cheaper than a full reload for experiments with many metrics, and is an easy way to check guardrails or analyze follow-up questions post-hoc.
### Use Statsig's macros
In Metric and Assignment sources, you can use Statsig Macros to directly inject a DATE() type that is relative to the experiment period being loaded.
- `{statsig_start_date}`
- `{statsig_end_date}`
For example, in an incremental reload from `2023-09-01` to `2023-09-03`, this query:
```sql
SELECT
user_id,
event,
ts,
dt
FROM log_table
WHERE dt BETWEEN {statsig_start_date} AND {statsig_end_date}
```
resolves to
```sql
SELECT
user_id,
event,
ts,
dt
FROM log_table
WHERE dt BETWEEN DATE('2023-09-01') AND DATE('2023-09-03')
```
This is a powerful tool since you can inject filters into queries with joins or CTEs and be confident that the initial scan will be pruned.
### Avoid contention
Resource contention is a common problem for Data Engineering teams. Large runs in the morning to calculate the previous day's data or reload tables are typical. On warehouses with flat resources or scaling limits, Pulse queries can be significantly slower during these windows, and can also slow down core business logic pipelines.
The best practice is to assign a scoped resource to Statsig's service user. This has a few advantages:
- Costs are easy to understand, because anything billed to that resource is attributable to Statsig.
- You can control the maximum spend by controlling the size of the resource, and independently scale the resource as your experimentation velocity increases.
- Statsig jobs don't affect your production jobs, and vice versa.
If this isn't possible:
- Schedule your Statsig runs after your main runs (this also ensures the data in your experiment analysis is fresh).
- Use API triggers to launch Statsig analyses after the main run is finished.
## Analytics optimization
When using Statsig’s Metric Explorer to visualize data in your warehouse, optimizing table layout and clustering configurations can greatly improve latency. This section describes best practices you can use to improve the performance of analytics queries, with recommendations for some of the most commonly used warehouses.
### BigQuery
#### Table layout: partitioning and clustering
Partition on event date and cluster on event when defining your events table. This improves performance because most queries filter for the event name and the time it was logged. When defining the partition on event date, truncate the timestamp to day-level granularity instead of using the raw timestamp (which would otherwise have millisecond precision, resulting in very high cardinality).
```sql
-- Create an events table partitioned by date and clustered by event.
CREATE TABLE dataset.events (
ts TIMESTAMP NOT NULL,
event STRING NOT NULL,
...
)
PARTITION BY DATE(ts)
CLUSTER BY event;
```
BigQuery's support for applying a cluster to an existing table is limited. Adding a cluster to an existing table doesn't automatically recluster the data immediately. If you need to repartition on event date or add a cluster by event, create a new table with the correct partitions and clusters using your current table.
```sql
-- Using an existing events table, create a new table that is partitioned by date and clustered by event.
CREATE OR REPLACE TABLE dataset.events_new
PARTITION BY DATE(ts)
CLUSTER BY event
AS
SELECT *
FROM dataset.events;
-- (Optional) Swap the name of your new table with the old one for consistency.
DROP TABLE dataset.events;
ALTER TABLE dataset.events_new RENAME TO events;
```
### Databricks
#### (Preferred) Use liquid clustering
When making clustering decisions in your events table layout, [liquid clustering](https://docs.databricks.com/aws/en/delta/clustering) provides a flexible approach that allows you to modify your clustering keys without needing to manually rewrite existing data.
Use liquid clustering for your events table as follows:
```sql
-- Enable liquid clustering for your events table.
ALTER TABLE events SET TBLPROPERTIES ('delta.liquidClustering.enabled' = 'true');
-- Cluster on the event column.
ALTER TABLE events ALTER CLUSTER BY (event);
-- Trigger clustering using the OPTIMIZE command.
OPTIMIZE events;
```
If your events table is frequently being updated, Databricks recommends scheduling `OPTIMIZE` jobs every 1-2 hours. This incrementally applies liquid clustering to your table.
#### (Alternative) Partitioning and ZORDER
If you choose not to use liquid clustering, partition on a single low-cardinality column such as the event date. Avoid adding more than one column on the partition unless necessary, and don't partition on a column with cardinality exceeding one thousand. Use a generated column to simplify pruning:
```sql
-- Define a partition on the event date generated column.
CREATE TABLE events (ts TIMESTAMP, event STRING, event_date DATE GENERATED ALWAYS AS (CAST(ts AS DATE))) USING DELTA PARTITIONED BY (event_date);
```
Use `ZORDER` to colocate similar values within a file for a high-cardinality column, which improves query performance through data skipping. Apply this to the event column:
```sql
-- ZORDER by the event column to improve data skipping.
OPTIMIZE events ZORDER BY (event);
```
Use a scheduled job that runs `OPTIMIZE` on the last week's event data to improve query performance by compacting small data files into fewer, larger files:
```sql
-- Periodically optimize the events table based on the last week of data.
OPTIMIZE events WHERE event_date >= current_date() - INTERVAL 7 DAYS;
```
Avoid partitioning on a combination of event date and event. Even for small queries, this creates significant overhead: with 500 events, a 30-day query hits 15,000 partitions. Instead, partition only by date as described above and use `ZORDER` on the event.
For extra parallelism without introducing too many partitions, `ZORDER` by an additional bucket column, defined as follows:
```sql
-- Add bucket column for extra parallelism.
ALTER TABLE events ADD COLUMN event_bucket INT GENERATED ALWAYS AS (pmod(hash(event), 16));
-- Rewrite data into new files based on this ZORDER key.
OPTIMIZE events ZORDER BY (event_bucket);
```
#### Partition pruning
Dynamic File Pruning enables Spark to prune partitions based on filter values at runtime. Enable it if possible using:
```sql
-- Turn on Dynamic File Pruning.
SET spark.databricks.optimizer.dynamicFilePruning = true;
```
#### Handling skew and joins
To rebalance skew and adjust join strategy, turn on Adaptive Query Execution:
```sql
-- Turn on Adapative Query Execution
SET spark.sql.adaptive.enabled = true;
```
#### Compute choices
The Photon query engine allows faster query execution with more efficient use of CPU and memory. If possible, enable it for your compute cluster or SQL warehouse.
### Redshift
#### Table design: distribution style and sort key
Because Redshift doesn't support partitioning by column, use a sort key instead. Most event tables are append-only and time-based, so use a compound sort key on (timestamp, event) to order data by time. Because all analytics queries filter on timestamp, this allows queries to read only the relevant blocks.
Ordering secondarily by event groups rows by event within each time block, which can reduce scan size when filtering by event. Timestamp is kept as the first key so pruning is primarily on timestamp.
Because there can be significant skew among event types, use an automatic distribution style rather than distributing on the event column. This allows Redshift to make distribution decisions based on the size of the table and query patterns. Create an events table with these recommendations as follows:
```sql
-- Create an events table with an automatic distrbution style and sort key on timestamp, event.
CREATE TABLE events (
event VARCHAR NOT NULL,
ts TIMESTAMP NOT NULL,
...
)
DISTSTYLE AUTO
COMPOUND SORTKEY (ts, event);
```
If you already have an events table and want to use the recommended distribution style and sort key, you can't apply those changes by modifying the existing table. Instead, create a new table and copy over data:
```sql
-- Create a new events table with the preferred distribution style and sort key.
CREATE TABLE events_new (
event VARCHAR NOT NULL,
ts TIMESTAMP NOT NULL,
...
)
DISTSTYLE AUTO
COMPOUND SORTKEY (ts, event);
-- Copy over data from the old events table.
INSERT INTO events_new (event, ts, ...)
SELECT event_id, ts, ...
FROM events;
-- Rename the two tables in a single transaction.
BEGIN;
ALTER TABLE events RENAME TO events_old;
ALTER TABLE events_new RENAME TO events;
COMMIT;
-- Drop the old table.
DROP TABLE events_old;
```
### Snowflake
#### Clustering keys
Because Snowflake doesn't allow explicit partitioning, use clustering keys on the event date and event to improve performance, as most queries filter for the event name and the time it was logged. Cluster on the timestamp truncated to day-level granularity instead of the raw timestamp (which would otherwise have millisecond precision, causing very high cardinality).
```sql
-- Cluster events on the timestamp truncated to day and event.
ALTER TABLE events CLUSTER BY (DATE_TRUNC('day', ts), event);
```
If you have a high cardinality of unique event types, it might be advisable to add a search optimization on event instead of clustering by that column. Go to the [next section](#managing-high-cardinality-columns) for details.
#### Managing high cardinality columns
If there are high-cardinality columns that you frequently reference in Metrics Explorer filters, consider using search optimization rather than clustering on those columns. Clustering on high-cardinality columns creates an overly large range of values for each micro-partition, making pruning inefficient. Search optimization generates an auxiliary index for fast lookups instead.
```sql
-- Add search optimization for fast lookups.
ALTER TABLE events ADD SEARCH OPTIMIZATION ON EQUALITY(some_column);
```
#### Monitoring clustering
Use `SYSTEM$CLUSTERING_INFORMATION` to check whether your current clustering scheme is effective. Large values of `average_depth` and `average_overlaps` may indicate the existing table needs reclustering on different keys with lower cardinality.
```sql
-- Given an events table clustered on event date and event, check the clustering information.
SELECT SYSTEM$CLUSTERING_INFORMATION('YOUR_DATABASE.YOUR_SCHEMA.EVENTS', '(DATE_TRUNC(''day'', ts), event)');
```
#### Timestamp column
If possible, use the `TIMESTAMP_NTZ` (no timezone) type for your timestamp column. This saves space because Snowflake doesn't need to store offset metadata. It can also speed up queries by allowing Snowflake to skip timezone normalization during filtering and pruning of micro-partitions.
### Questions
For additional support optimizing your warehouse configuration for analytics, reach out in the Slack support channel for your organization within Statsig Connect.
## Debugging
Statsig shows you all the SQL being run and any errors that occur. These are generally caused by changes to underlying tables or Metric Sources that cause a metric query to fail. Here are some best practices for debugging Statsig queries.
### Use the queries from your Statsig console
If a Pulse load fails, find all the SQL queries and associated error messages in the *Diagnostics* tab. Select the copy button to run or debug the query in your warehouse. Common errors include:
- A query attempting to access a field that no longer exists on a table.
- A table not existing, usually because the Statsig user doesn't have permission on a new table.
## Turbo mode
[Turbo Mode](../features/turbo) skips some enrichment calculations (in particular some time series rollups) to compute the latest snapshot of your data at low cost. Customers have run experiments on 150+ million users in less than 5 minutes on a Snowflake S cluster.
### Contact support
Statsig's support team is responsive and can help you fix your issue and build tools to prevent it in the future, whether it's due to system or user error.
## Compute cost transparency
Statsig Warehouse Native lets you get an overview of the compute time experiment analysis incurs in your warehouse. Break this down by experiment, metric source, or type of query to find what to optimize. Common questions the dashboard is designed to answer include:
- Which Metric Sources take the most compute time?
- What is the split of compute time between full loads, incremental loads, and custom queries?
- How is compute time distributed across experiments?
You can find this dashboard in the Left Nav under Analytics -> Dashboards -> Pipeline Overview

This dashboard is built using Statsig Product Analytics. You can customize any of these charts or build new ones. A useful addition is to include your average compute cost, so you can convert slot time per experiment into dollar cost per experiment.
At the end of every Pulse load / DAG, Statsig uploads a single row to the `pipeline_overview` table for each job executed as part of that run. This table has the following schema:
| Column | Type | Description |
| -------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------ |
| ts | timestamp | Timestamp at which the DAG was created. |
| job_type | string | Job type (see [Pipeline Overview](/statsig-warehouse-native/analysis-tools/pipeline-overview/)) |
| metric_source_id | string | Only applicable for 'Unit-Day Calculations' jobs - the ID of the metric source |
| assignment_source_id | string | The Assignment Source ID of the experiment for which Pulse was loaded. |
| job_status | string | The final state of the job (`fail` or `success`) |
| metrics | string | Metrics processed by the job |
| dag_state | string | Final state of the DAG (`success`, `partial_failure`, or `failure`) |
| dag_type | string | Type of DAG (`full`, `incremental`, `metric`, `power`, `custom_query`, `autotune`, `assignment_source`, `stratified_sampling`) |
| experiment_id | string | ID of the experiment for which Pulse was loaded, if applicable |
| dag_start_ds | string | Start of the date range being loaded |
| dag_end_ds | string | End of the date range being loaded |
| wall_time | number | Total time elapsed between DAG start and finish, in milliseconds |
| turbo_mode | boolean | Whether the DAG was run in Turbo Mode |
| dag_id | string | Internal identifier for the DAG |
| dag_duration | number | Number of days in the date range being loaded |
| is_scheduled | boolean | Whether the DAG was triggered by a scheduled run |
================================================================================
# Data & Semantic Layer
URL: https://docs.statsig.com/statsig-warehouse-native/configuration/data-and-semantic-layer
Updated: 2026-07-11
================================================================================
When using Statsig Warehouse Native, you configure metrics, experiments, and other schematized data objects that allow Statsig to perform analysis on top of your warehouse.
This functions as a lightweight Semantic Layer, which can integrate with popular Semantic Layer tools like Cube or in-house YAML-based stores. This section covers the building blocks of Statsig's configuration layer.
{% figure %}

{% /figure %}
================================================================================
# Metric Sources
URL: https://docs.statsig.com/statsig-warehouse-native/configuration/metric-sources
Updated: 2026-07-11
================================================================================
Metric Sources are how you schematize your warehouse data for Statsig, and they serve as the input data for metrics.
## What is a metric source
A metric source is a key part of Statsig's semantic layer (and integrations to other Semantic Layers). A metric source consists of a data source, which is either:
* A SQL Query that Statsig reads as a view
* A warehouse table
And configuration around the source:
* **\[Required]** identifier and timestamp columns
* **\[Optional]** Aliases, partition information
* **\[Early Access]** Data quality checks and configuration
This is the entry point for your data to be used in parameterized queries for experiment analysis, analytics, and more within the Statsig console.
## Data sources
### Getting data
Statsig Metric Sources can use a query or a table as the source of their data.
A query-based source reads a SQL query and uses it as a source of truth. Combined with Statsig's built-in [query macros](/statsig-warehouse-native/configuration/query-tools), this provides an efficient and flexible way to create experimental data sources.
{% figure %}

{% /figure %}
For larger or managed datasets, use table sources. Table sources minimize data scan and provide a more 1:1 mapping of "data source" to "metric source". Statsig date macros are automatically applied in each experiment result reload when you use table sources.
{% figure %}

{% /figure %}
### Configuring data
For any source, select which field to use for the timestamp of the logs or metrics being provided, as well as 1 to N user identifiers to join to experiment data.
{% figure %}

{% /figure %}
For table sources, you can optionally provide a partitioning column to reduce data scan, and provide aliases to make your column names more human-readable.
{% figure %}

{% /figure %}
### Types of data
Statsig works natively with many different types and granularities of data. Common patterns are:
#### 1. Raw event logging (event level data), using the log timestamp as the timestamp, example:
| `event_time` | `user_id` | `event_name` | `platform` | `value` |
|-----------------------|-----------|------------------|------------|---------|
| 2024-03-01 10:05:12 | `u_123` | `page_view` | `web` | `null` |
| 2024-03-01 10:05:45 | `u_123` | `button_click` | `web` | `null` |
| 2024-03-01 10:07:02 | `u_456` | `purchase` | `ios` | 29.99 |
*Metric examples with this source:*
*- Number of users with purchase: You can create a [UNIT COUNT](/statsig-warehouse-native/metrics/unit-count-once) metric, with a filter of event\_name = 'purchase'.*
*- % of users with page view who clicked: You can create a [RATIO](/statsig-warehouse-native/metrics/ratio) metric with this metric source in both denominator and numerator, and apply event\_name filter accordingly.*
#### 2. Fact tables (one row per entity per day), using the date of the row as the timestamp, example:
| `order_date` | `order_id` | `user_id` | `order_status` | `items_count` | `revenue` |
|--------------|------------|-----------|----------------|---------------|-----------|
| 2024-03-01 | `o_10001` | `u_123` | `completed` | 2 | 49.98 |
| 2024-03-01 | `o_10002` | `u_456` | `completed` | 1 | 19.99 |
| 2024-03-02 | `o_10003` | `u_123` | `refunded` | 1 | 19.99 |
*Metric examples with this source:*
*- Revenue: You can create a [SUM](/statsig-warehouse-native/metrics/sum) metric on 'revenue' column, with any filters you need.*
*- Average order value: You can create a [MEAN](/statsig-warehouse-native/metrics/mean) metric on 'revenue' column, with any filters you need.*
#### 3. Aggregated fact tables at unit day granularity, using the date of the row as the timestamp, example:
| `date` | `user_id` | `sessions` | `purchases` | `revenue` |
|--------------|-----------|------------|-------------|-----------|
| 2024-03-01 | `u_123` | 3 | 1 | 29.99 |
| 2024-03-01 | `u_456` | 1 | 0 | 0.00 |
| 2024-03-02 | `u_123` | 2 | 0 | 0.00 |
*Metric examples with this source:*
*- Revenue: You can create a [SUM](/statsig-warehouse-native/metrics/sum) metric on 'revenue' column, with any filters you need.*
*- Number of users with purchase: You can create a [UNIT COUNT](/statsig-warehouse-native/metrics/unit-count-once) metric, with a filter of purchase > 0.*
### Types of data that require transformation
You may have a table that doesn't fit the schemas described above. For example, a wide user dimension table contains one row per user with pre-aggregated or derived behavioral attributes.
| `user_id` | `signup_date` | `first_active_date` | `first_page_view_date` | `first_purchase_date` | `last_active_date` | `lifetime_revenue` | `is_power_user` |
|-----------|---------------|---------------------|------------------------|-----------------------|--------------------|--------------------|-----------------|
| `u_123` | 2023-11-12 | 2023-11-12 | 2023-11-12 | 2023-11-20 | 2024-03-02 | 249.85 | `true` |
| `u_456` | 2024-01-05 | 2024-01-06 | 2024-01-05 | 2024-02-10 | 2024-02-18 | 19.99 | `false` |
| `u_789` | 2024-02-10 | 2024-02-10 | 2024-02-10 | `null` | 2024-03-01 | 89.97 | `false` |
This table type isn’t compatible with Statsig because it doesn’t have a single timestamp column that you can consistently configure across all events (for example, `signup`, `first_active`, and `last_active`). Statsig’s stats engine relies on a timestamp column to join metric data with exposure timestamps, ensuring that Statsig includes only metric events that occur after a user is exposed to an experiment.
For example, suppose you configure `signup_date` as the timestamp column for this metric source. If you then attempt to build a funnel metric across multiple events (for example, `signup` → `first_active` → `first_purchase`), Statsig treats all funnel steps as occurring at the signup date, because the metric source can only use a single configured timestamp column. As a result, Statsig ignores event-specific timestamps in other columns (such as `first_active_date` or `first_purchase_date`), and the funnel no longer reflects the true timing of each event.
To address this, there are two recommended options:
* **Use upstream tables as the metric source**: Define metrics directly from event logs or fact tables where each row represents a single event and includes a clear timestamp.
* **Collapse the table into a long format**: Reshape the wide user table into a long table (for example, one row per user per event or per day) with a unified timestamp column that you can configure in Statsig.
| `user_id` | `event_type` | `event_timestamp` |
|-----------|------------------|-----------------------|
| `u_123` | `signup` | 2023-11-12 09:15:00 |
| `u_123` | `first_active` | 2023-11-13 10:02:41 |
| `u_123` | `last_active` | 2024-03-02 18:45:10 |
| `u_456` | `signup` | 2024-01-05 14:22:09 |
| `u_456` | `first_active` | 2024-01-06 08:11:54 |
## Managing metric sources
In the metric source tab, you can see your metric sources and the metrics and experiments that use them. In some cases, it makes sense to have a broad metric source that you reuse across many metrics with different filters and aggregations. In others, a metric source may exist for one metric, such as a set of specific events for a funnel.
{% figure %}

{% /figure %}
### Programmatic updates
You can create and modify metric sources through the API as part of your release flow for data systems. This supports the creation of read-only artifacts. Refer to the [console API](/statsig-warehouse-native/configuration/console-api) and [Semantic Layer Sync](/statsig-warehouse-native/configuration/semantic-layer-sync) documentation.
### Governance
If you're concerned about granting Statsig broad warehouse access, give Statsig access only to its own staging schema or dataset, and create views or materialize staging tables in that location for the data you want Statsig to read.
## Daily vs. realtime sources
When specifying a timestamp, you can also specify if the metric source contains data at a daily or timestamp granularity by toggling the "Treat Timestamp as Date" setting.
{% figure %}

{% /figure %}
When this setting is **not** enabled, the system performs a timestamp-based join. Statsig attributes events to the experiment results based on the exact time they occur relative to the exposure time. For example, if a user is exposed at `2024-01-01T11:00:00` and an event occurs at `2024-01-01T11:01:00`, Statsig attributes the event to the experiment because it occurred after the exposure. If the event occurs at `2024-01-01T10:59:00`, before the exposure, Statsig doesn't attribute it.
When the "Treat Timestamp as Date" setting is enabled, the system performs a date-based join. All events occurring on the same calendar day as the exposure, regardless of time, are included in the experiment results. This includes data from the first day of exposures, ensuring Statsig doesn't omit day-1 metrics from the analysis.
All Statsig needs to create metrics is a timestamp or date, and a unit (or user) identifier. Context fields let you pull multiple metrics from
the same base query, and select values to sum, mean, or group by.
| Column Type | Description | Format/Rules |
| ---------------------- | ------------------------------------------------------------------- | ------------------------------ |
| timestamp | **Required** an identifier of when the metric data occurred | Castable to Timestamp/Date |
| unit identifier | **Required** At least one entity to which this metric belongs | Generally a user ID or similar |
| additional identifiers | *Optional* Entity identifiers for reuse across identifier types | |
| context columns | *Optional* Fields which will be aggregated, filtered, or grouped on | |
For example, you could pull from event logging and aggregate the event-level data to create metrics:
| timestamp | user\_id | company\_id | event | time\_to\_load | page\_route |
| ------------------- | ------------- | ---------- | ---------- | ------------ | ---------- |
| 2023-10-10 00:01:01 | my\_user\_17503 | c\_22235455 | page\_load | 207.22 | / |
| 2023-10-10 00:02:15 | my\_user\_18821 | c\_22235455 | page\_load | 522.38 | /search |
| 2023-10-10 00:02:22 | my\_user\_18821 | c\_22235455 | serp\_click | null | /search |
You could create an average TTL metric by averaging time\_to\_load, and group it by page route or filter to specific routes when creating your metric.
As another example, you might pre-calculate some metrics at a user-day grain, either to match your source of truth exactly or to add more complex logical fields:
| timestamp | user\_id | company\_id | country | page\_loads | satisfaction\_score | revenue\_usd | net\_revenue\_usd |
| ---------- | ------------- | ---------- | ------- | ---------- | ------------------ | ----------- | --------------- |
| 2023-10-10 | my\_user\_17503 | c\_22235455 | US | 13 | 9 | 130.21 | 112.33 |
| 2023-10-10 | my\_user\_18821 | c\_22235455 | CA | 1 | 2 | 0 | 0 |
| 2023-10-10 | my\_user\_18828 | c\_190887 | DE | 0 | null | 22.1 | 0 |
You can create different metrics by summing and filtering on those daily fields.
## (Very) slow metric sources
Statsig uses techniques like Statsig macros, push-down filters (predicate filters), and partition keys to make queries in your warehouse efficient. While metric sources can include joins or complex queries, they should be performant. If they aren't, any metrics based on that metric source become expensive or cause timeouts and failures. The same applies to assignment sources.
Statsig flags a metric source as slow if it takes more than 30 seconds to retrieve a sample of up to 100 records from the table. If the query is expensive, consider the following steps in sequence to optimize your metric source:
* Include filters based on the partition column.
* Use [Statsig macros](/statsig-warehouse-native/guides/best-practices#use-statsigs-macros) in SQL.
* Pre-calculate some of the metrics to avoid joins or complex queries.
* Upgrade your computing resources if you are on a very small cluster. Do this cautiously.
{% callout type="note" %}
If Statsig flagged you for a slow assignment source, the same guidance applies.
{% /callout %}
================================================================================
# Metrics Overview
URL: https://docs.statsig.com/statsig-warehouse-native/configuration/metrics
Updated: 2026-07-11
================================================================================
Metrics are measures of user or system behavior that serve as evaluation criteria for
experiments and for performing analysis.
Metrics are organized within your Metrics Catalog.
{% figure %}

{% /figure %}
## Creating metrics
Metrics are a combination of a metric source, an aggregation, and optional filters and advanced settings. The metric source provides the raw data, and the aggregation defines how Statsig aggregates data across different granularities: user-level, group-level, or daily timeseries.
Metrics can support multiple units of analysis. For example, you can use a revenue metric for a "User Level" and a "Store Level" experiment, as long as the metric source has a mapping for both ID types.
Filters are also a core component of metrics. Statsig offers a rich set of filtering options, including SQL-based filters, so you can reuse the same metric source for many use cases.
{% figure %}

{% /figure %}
## Using metrics
You can use metrics for standalone analysis, as part of your experiment scorecard, or as guardrails for feature releases. You can also group them into collections using tags for easy addition across these surfaces. You can also configure metrics to send alerts globally if any experiment or gate causes a regression.
Statsig recommends using tags extensively. A combination of team-level and surface- or product-level tags ensures easy discovery of metrics.
To view details about a metric, navigate to the Metrics page to see its definition, related experiments, and a timeseries of the metric value.
{% figure %}

{% /figure %}
In the [insights tab](/experiments/exploring-results/aggregated-impact) and in [meta-analysis](/statsig-warehouse-native/features/meta-analysis), you can perform more detailed analysis of how experiments have impacted a metric, and how the metric relates to other metrics in your catalog.
{% figure %}

{% /figure %}
## Loading metrics
Selecting **Reload Metric Data** causes Statsig to sync your latest data and display it on this page. The default loading window is 90 days, but you can configure a longer period if needed.
{% figure %}

{% /figure %}
To use metrics to measure topline impact, or for ongoing tracking in the metrics page, schedule loads of the metric values. You can trigger a load manually from the metric page, or schedule it from there (3-dot menu > edit scheduled reload). You can also configure this as a project-level setting under Data Connection in your project settings.
## Metric management
Without a well-managed Metric Catalog, end users may not know whether they can trust a metric or understand how it's defined. Statsig provides several tools to address this:
* \[Verified Metrics] and programmatic management allow you to vet your core metrics and make it clear which metrics set the gold standard
* \[[RBAC](/statsig-warehouse-native/features/roles-and-access) and Team Ownership] let you limit the number of potential editors for Metrics, keeping control of core definitions in the experiment team's hands
* \[Local Metrics] in Pulse or Explore queries allow you to make experiment-scoped and clearly-labeled changes to metric definitions without adding a large amount of single-user metrics to your metrics catalog
* Scorecard hover-over definitions make it trivial to discover, in-context, what settings were applied to a metric and how the calculation was performed.
## Metric types
Statsig offers the largest coverage of metric types of any enterprise platform. These fall into 4 categories:
### Aggregations
Aggregations are basic unit-level counts or sums that measure user behavior. You can use them as inputs to ratios, especially for [cluster-based experiments](/metrics/different-id) where you want to normalize measures.
Statsig computes aggregations at the unit level, then averages them across all units in the experiment during Pulse analysis.
Supported aggregations are:
* [Count](/statsig-warehouse-native/metrics/count)
* [Sum](/statsig-warehouse-native/metrics/sum)
* [Logged Sums and Counts](/statsig-warehouse-native/metrics/log)
* [Count Distinct](/statsig-warehouse-native/metrics/count-distinct)
* [First Value](/statsig-warehouse-native/metrics/latest-value#first-value)
* [Latest Value](/statsig-warehouse-native/metrics/latest-value)
* Thresholds
* A special case of sum/count that measures a 1/0 flag for whether a user passed a threshold value during the experiment - e.g., "the number of users who spent more than $100"
### Unit counts, retention, and conversion
Statsig offers many ways to count units in an experiment. This allows you to measure questions like:
* Did users sign up more often?
* Are more users currently subscribers at the end of the experiment?
* How many users refunded in the first week of the experiment?
These all fall under the "Unit Count" type, with rollups specifying calculation details. Unit counts produce a 1/0 flag or a sum of user-days at the user level, then Statsig averages them across all units in the experiment during Pulse analysis.
Supported unit count types are:
* [One-Time Event](/statsig-warehouse-native/metrics/unit-count-once), measuring if a user performed an action at all during the experiment
* [Windowed](/statsig-warehouse-native/metrics/unit-count-window), measuring if a user performed an action within some time window after exposure
* [Latest Participation](/statsig-warehouse-native/metrics/unit-count-latest), measuring if a user fulfilled some criteria on their latest recorded record (e.g. is this user currently a subscriber)
* [Daily Participation](/statsig-warehouse-native/metrics/unit-count-rate), the rate at which units were daily active users during the experiment
* [Retention](/statsig-warehouse-native/metrics/retention), the rolling rate of retention on an action or set of actions with a configurable time window
### Ratios and funnels
Ratios and funnels measure rates or conversion. Statsig aggregates these differently: they measure the total of a numerator metric divided by the total of a denominator metric across each experiment group. They also apply the delta method to correct for covariance between the component metrics.
[Ratio Metrics](/statsig-warehouse-native/metrics/ratio) allow you to measure ratios of two different metrics, giving nuance to results and helping you to normalize results by another measure - for example:
* B2C: Average purchase revenue (`SUM(Revenue) / COUNT(orders)`)
* B2B: Revenue per User (`SUM(Revenue) / COUNT_DISTINCT(user_id)`)
[Funnel Metrics](/statsig-warehouse-native/metrics/funnel) allow you to analyze user conversion through multi-step flows at an overall and stepwise level. Funnel metrics help you identify dropoff points in user journeys and measure conversion rates. Statsig offers session-level funnels, which measure conversion within distinct checkout flows, support conversations, or viewing sessions.
### Performance metrics
Teams working on performance problems use Statsig to analyze the impact of their changes at a system level. These metric types can also be useful for user behavior. The types most commonly used here are:
* [Percentile Metrics](/statsig-warehouse-native/metrics/percentile) allow you to measure changes in values like the P99.9 - useful for measuring improvements or regressions in latency, TTL, or measuring median change when the mean is skewed
* [Mean Metrics](/statsig-warehouse-native/metrics/mean) are an easy shorthand for ratio metrics summing an event-level value and dividing by the total records
## Filters
Statsig offers many ways to filter your data. You can write filters in SQL, but leaving metric sources broad and allowing users to construct filters in the UI or through semantic layer syncs gives greater no-code flexibility for metric creation and follow-up analysis, especially when using [local metrics](/metrics/local-metrics).
### Any of / None of
These are the equivalent to a SQL `IN` or `NOT IN` statement, and you can also use them for equality/inequality. You can supply 1 to N values to the filter to include or exclude from your result set.
### Inequalities
You can compare numerical values using `=`, `>`, `<`, `>=`, or `<=`. Equality can also compare non-numerical values, which Statsig evaluates as a string-casted comparison.
### Null checks
Statsig supports checks for `Is Null` or `Non Null`, which can be useful for identifying if a flag was logged or for filtering out partial data.
### Contains/Does Not Contain/Starts With/Ends With
These operators are similar to a SQL `LIKE` operator:
* Contains checks for if a substring is in a string, e.g. \`field LIKE '%search\_string%'
* Does Not Contain checks for if a substring is in a string, e.g. \`field not LIKE '%search\_string%'
* Starts With checks for if a substring starts the field, e.g. \`field LIKE 'search\_string%'
* Ends With checks for if a substring ends the field, e.g. \`field LIKE '%search\_string'
### Is After Exposure
This filter allows you to specify a **secondary** date/timestamp field that has to come after the user's enrollment to the experiment.
By default, Statsig only considers metric data where the primary metric timestamp is after the user first saw the experimental intervention. However, you might have another field like "first\_saw\_content\_at". You can use `Is After Exposure` to enforce that this secondary timestamp also takes place after the user's exposure.
### SQL filters
SQL filters allow you to inject any SQL filter string into your metric definition, which Statsig validates before saving. This is flexible and lets you interact with complex objects or do complex logic operations as needed to define your metrics.
For example, the sql filter `(weight_lb)/pow(height_inches, 2) > 25` would be added to your metric source query for this metric as:
```sql
SELECT
FROM
WHERE AND ((weight_lb)/pow(height_inches, 2) > 25)
```
## Settings
Each metric type's page has specific information on settings relevant to that metric. This section introduces common settings across metrics.
### Breakdowns
Most aggregation-type metrics (sum, count, count distinct, unit count, means, ratios, percentiles, first/latest, min, max) allow you to generate a breakdown view of any column automatically. You can specify this column in the metric set up, and then click into the experiment result to see the breakdown.
### Cohorts
Cohort settings allow you to specify a window for data collection after a unit's exposure. For example, a 4-6 day cohort window would only count actions from days 4, 5, and 6 after a unit was exposed to an experiment.
Refer to the [cohort documentation](/statsig-warehouse-native/features/cohort-metrics) for full details.
### Baking
Many metric types support baking. Statsig waits to calculate baked metrics and uses "old" data for them. This is appropriate for cases like credit card chargebacks, where you may adjust your payments dataset to account for chargebacks in a "net revenue" metric. For additional information, go to the [cohort documentation](/statsig-warehouse-native/features/cohort-metrics).
Statsig:
* doesn't calculate baked metrics until the bake period has elapsed since the user's enrollment
* pulls historical data that "just baked" as of that day on a given load
* only calculates results for users whose bake window has elapsed to avoid diluting metrics
This way, Statsig doesn't show incomplete data in Pulse (either because it is incomplete in the warehouse, or because of late-landing data).
### Thresholding
Thresholding converts a sum, count, count-distinct, max/min, or first/latest metric into a 1/0 unit metric based on whether a user's total value meets a specified threshold. For example, to measure how many users spent at least $100 in their first week, specify a 0-6 day cohort metric that sums revenue with a threshold of $100.
### CUPED
You can enable/disable CUPED per metric, and also specify the lookback window for which Statsig pulls users' pre-exposure data and uses it as an input to the CUPED regression.
### Winsorization
Go to [winsorization](/experiments/statistical-methods/methodologies/winsorization) for full details. Winsorization applies to aggregate metrics and allows you to specify an upper or lower percentile-based threshold, clamping data to that threshold. This reduces inflated variance or skewed means caused by outlier data or buggy logging.
### Capping
Capping is an alternate outlier-control method that clamps values to a unit-day cap, specifiable per unit type. For example, you might clamp "total purchase count" to 100 on an auction platform to prevent resellers from skewing your metrics, assuming 100 is a reasonable upper bound for typical users.
================================================================================
# Sum Metrics
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/sum
Updated: 2026-07-11
================================================================================
## Use cases
Sums are a common metric type, suited for comparing behavior across groups. Common examples are:
* Totalling revenue by summing a `revenue` column from purchase logs
* Totalling time spent by summing an aggregated `time_spent` metric on a user-day fact table
## Calculation
At the unit level, sum metrics take the SUM of their input column.
At the group level, Statsig calculates the mean as the SUM of the unit-level sums, divided by the count of UNIQUE UNITS exposed to the experiment.
The SQL for this calculation:
```sql expandable
-- Unit Level
SELECT
unit_id,
group_id,
SUM(value_column) as value
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
GROUP BY
source_data.unit_id,
exposure_data.group_id;
-- Experiment
SELECT
group_id,
COUNT(distinct unit_id) total_units
FROM exposure_data
GROUP BY group_id;
-- Group Level
SELECT
group_id,
SUM(value)/SUM(total_units) as mean
FROM unit_data
JOIN group_data
USING (group_id)
GROUP BY group_id;
```
### Methodology notes
Sum metrics use the SQL SUM aggregation. See the Options section below for many advanced options.
## Options
* Metric Breakdowns: configure Metadata Columns to group results by for dimensional views in pulse results.
* Multi Source: build sums from multiple metric sources, equivalent to a UNION in SQL. Useful when the same measure exists in sharded or disparate tables.
* Winsorization: specify a lower and/or upper percentile bound. All values below the lower threshold or above the upper threshold are clamped to reduce the impact of outliers.
* Capping: specify an exact cap value (per unit type) to limit a unit's daily value. For example, cap daily purchases at 100 on an e-commerce site to exclude reseller behavior.
* CUPED: enable CUPED and specify the lookback window for pre-experiment data inputs.
* Thresholding: convert this metric into a 1/0 unit count metric that flags whether the unit's total sum meets or exceeds a given threshold.
* [Cohort Windows](/statsig-warehouse-native/features/cohort-metrics): specify a window for data collection after a unit's exposure. For example, a 0-1 day cohort window counts only actions from days 0 and 1 after exposure. Select **Only include units with a completed window** to exclude units from pulse analysis until their cohort window has completed.
* [Baked Metrics](/statsig-warehouse-native/features/cohort-metrics#metric-bake-windows): specify how long a metric needs to mature. This is common for chargebacks or cancellations. Statsig delays loading the data until the window has elapsed, and calculates pulse results only after a unit's metric has matured.
================================================================================
# Count Metrics
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/count
Updated: 2026-07-11
================================================================================
### Use cases
Count metrics are commonly used to measure how often events occurred. Common examples are:
* Counting click frequency by counting event logs filtered to the `click` event
* Making a threshold metric of if a user read more than 2 articles
## Calculation
At the unit level, count metrics run a COUNT(1) across their metric source.
At the group level, Statsig calculates the mean as the SUM of the unit-level count, divided by the count of UNIQUE UNITS exposed to the experiment.
The SQL looks like the following:
```sql expandable
-- Unit Level
SELECT
source_data.unit_id,
exposure_data.group_id,
COUNT(1) as value
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
GROUP BY
source_data.unit_id,
exposure_data.group_id;
-- Experiment
SELECT
group_id,
COUNT(distinct unit_id) total_units
FROM exposure_data
GROUP BY group_id;
-- Group Level
SELECT
group_id,
SUM(value)/SUM(total_units) as mean
FROM unit_data
JOIN group_data
USING (group_id)
GROUP BY group_id;
```
### Methodology notes
Count metrics use the SQL COUNT aggregation. Many advanced options are available.
## Options
* Metric Breakdowns
* You can configure Metadata Columns to group results by, getting easy access to dimensional views in pulse results
* Multi Source
* Counts can be built with inputs from multiple metric sources; think of this as a UNION in SQL. This can be useful if you have the same measure in sharded or disparate tables
* Winsorization
* Specify a lower and/or upper percentile bound to winsorize at. Statsig clamps all values below the lower threshold, or above the upper threshold, to that threshold to reduce the outsized impact of outliers on your analysis.
* Capping
* Specify an exact cap value (per unit type) to limit a unit's daily value for the count. For example, you might want daily purchases to be capped at a high value like 100 on an e-commerce site to ignore reseller behavior
* CUPED
* Specify if you want to calculate CUPED, and the lookback window for CUPED's pre-experiment data inputs
* Thresholding
* Turn this metric into a 1/0 unit count metric counting if the unit's total count equals to or surpasses (>=) a given threshold
* [Cohort Windows](/statsig-warehouse-native/features/cohort-metrics)
* You can specify a window for data collection after a unit's exposure. For example, a 0-1 day cohort window would only count actions from days 0 and 1 after a unit was exposed to an experiment
* Select **Only include units with a completed window** to remove units from pulse analysis for this metric until the cohort window has completed.
* [Baked Metrics](/statsig-warehouse-native/features/cohort-metrics#metric-bake-windows)
* [Baked Metrics](/statsig-warehouse-native/features/cohort-metrics#metric-bake-windows) let you specify how long a metric needs to mature. This is common in situations like chargebacks or cancellations. Statsig delays loading the data until the window has elapsed, and only calculates pulse results for that metric after a unit's metric has matured.
================================================================================
# Count Distinct Metrics
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/count-distinct
Updated: 2026-07-11
================================================================================
{% callout type="warning" %}
Count-distinct metrics are more expensive to compute than [count](./count) or [unique unit count](./unit-count-once) metrics, especially for very long experiments
If you want to count distinct occurrences of the experiment's unit of assignment (e.g. the user\_id in a user\_id experiment), you should use a unit\_count metrics instead. This achieves the same result, but more efficiently calculates and stores the metric data.
In many cases a count metric serves as a close proxy to count-distinct; you can also set up a data source to track unique instances of a key to avoid re-running the distinct operation across experiment analyses.
{% /callout %}
### Use cases
Count distinct metrics have two primary use cases:
* Measuring interactions and surface area. For example, you might count the number of entities a user has engaged with on a video streaming platform, or measure if a new recommendation engine increases the diversity of products clicked.
* As a denominator in ratio metrics, especially common when you want to normalize by a unit other than your experiment's unit of analysis. For example, a B2B experiment might run at the company level but measure "Clicks per USER" using a ratio metric of COUNT(clicks)/COUNT\_DISTINCT(user\_id).
## Calculation
At the unit level, count distinct metrics use COUNT\_DISTINCT on their input column.
At the group level, Statsig calculates the mean as the SUM of the unit-level COUNT\_DISTINCTs, divided by the count of UNIQUE UNITS exposed to the experiment.
The SQL looks like the following:
```sql expandable
-- Unit Level
SELECT
source_data.unit_id,
exposure_data.group_id,
COUNT(distinct source_data.value_column) as value
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
GROUP BY
source_data.unit_id,
exposure_data.group_id;
-- Experiment
SELECT
group_id,
COUNT(distinct unit_id) total_units
FROM exposure_data
GROUP BY group_id;
-- Group Level
SELECT
group_id,
SUM(value)/SUM(total_units) as mean
FROM unit_data
JOIN group_data
USING (group_id)
GROUP BY group_id;
```
### Methodology notes
In the metrics page view, Statsig uses APPROX\_COUNT\_DISTINCT (or equivalent) to avoid large compute jobs on analytical count distinct. The approximate error is acceptably small for topline estimates. For experiment result loads, the calculation is analytical and exact to avoid jitter or bias from approximation error.
## Options
* Metric Breakdowns
* You can configure Metadata Columns to group results by, getting easy access to dimensional views in pulse results
* Winsorization
* Specify a lower and/or upper percentile bound to winsorize at. Statsig clamps all values below the lower threshold, or above the upper threshold, to that threshold to reduce the outsized impact of outliers on your analysis.
* CUPED
* Specify if you want to calculate CUPED, and the lookback window for CUPED's pre-experiment data inputs
* Thresholding
* Turn this metric into a 1/0 unit count metric counting if the unit's total count equals to or surpasses (>=) a given threshold
* [Cohort Windows](/statsig-warehouse-native/features/cohort-metrics)
* You can specify a window for data collection after a unit's exposure. For example, a 0-1 day cohort window would only count actions from days 0 and 1 after a unit was exposed to an experiment
* Select **Only include units with a completed window** to remove units from pulse analysis for this metric until the cohort window has completed.
## Limits
Count distinct metrics are available in most experiments, except for Switchbacks.
================================================================================
# Unit Count (One-Time Event) Metrics
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/unit-count-once
Updated: 2026-07-11
================================================================================
## Use cases
This is an extremely common metric type, used to measure participation rates among users in the experiment.
## Calculation
At the unit level, unit count metrics create a 1/0 flag for if they participated.
At the group level, Statsig calculates the mean as the SUM of the unit-level flags, divided by the count of UNIQUE UNITS exposed to the experiment.
The SQL for this calculation:
```sql expandable
-- Unit Level
SELECT distinct
source_data.unit_id,
exposure_data.group_id,
1 as value
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
;
-- Experiment
SELECT
group_id,
COUNT(distinct unit_id) total_units
FROM exposure_data
GROUP BY group_id;
-- Group Level
SELECT
group_id,
SUM(value)/SUM(total_units) as mean
FROM unit_data
JOIN group_data
USING (group_id)
GROUP BY group_id;
```
### Methodology notes
Statsig calculates daily and days-since-exposure views for user-day metrics per day. If a user is active on all 14 days of an experiment, they contribute 1 to the overall cumulative numerator, but 1 to each day of the daily view. The daily view doesn't deduplicate users. This yields a more intuitive interpretation and produces less sparse timeseries data on long experiments. However, this can cause mix-shift effects where the daily trend moves in the opposite direction of the cumulative timeseries, because daily returning users have already been counted and don't add metric value to the cumulative view.
Unit count metrics use the SQL SUM aggregation. See the Options section below for many advanced options.
## Options
* Metric Breakdowns: configure Metadata Columns to group results by for dimensional views in pulse results.
* Multi Source: build metrics from multiple metric sources, equivalent to a UNION in SQL. Useful when the same measure exists in sharded or disparate tables.
* Rollup Mode: controls the specific way that Unit Count metrics are aggregated.
================================================================================
# Unit Count (Window) Metrics
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/unit-count-window
Updated: 2026-07-11
================================================================================
## Use cases
This is an extremely common metric type, used to measure participation rates early in experiments, or to measure retention later into an experiment (e.g. did a user come back in their second week).
## Calculation
At the unit level, unit count metrics create a 1/0 flag for if they participated during the time window. The time window is relative to the unit's first exposure. Statsig doesn't consider subsequent exposures.
At the group level, Statsig calculates the mean as the SUM of the unit-level flags, divided by the count of UNIQUE UNITS exposed to the experiment.
The SQL for this calculation:
```sql expandable
-- Unit Level
SELECT distinct
source_data.unit_id,
exposure_data.group_id,
MAX(if(source_data.in_time_window, 1, 0)) as value
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
GROUP BY
source_data.unit_id,
exposure_data.group_id;
;
-- Experiment
SELECT
group_id,
COUNT(distinct unit_id) total_units
FROM exposure_data
GROUP BY group_id;
-- Group Level
SELECT
group_id,
SUM(value)/SUM(total_units) as mean
FROM unit_data
JOIN group_data
USING (group_id)
GROUP BY group_id;
```
## Options
* Metric Breakdowns: configure Metadata Columns to group results by for dimensional views in pulse results.
* Multi Source: build metrics from multiple metric sources, equivalent to a UNION in SQL. Useful when the same measure exists in sharded or disparate tables.
* Rollup Mode: controls the specific way that Unit Count metrics are aggregated.
================================================================================
# Unit Count (Latest Value) Metrics
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/unit-count-latest
Updated: 2026-07-11
================================================================================
## Use cases
This metric type is useful for measuring state. For example, use it to determine whether a test variant currently has more active subscribers or active users than the control group.
## Calculation
At the unit level, unit count metrics create a 1/0 flag for if they participated on a given day. Statsig carries this forward to the current date if data is behind.
At the group level, Statsig calculates the mean as the SUM of the unit-level flags, divided by the count of UNIQUE UNITS exposed to the experiment.
The SQL for this calculation:
```sql expandable
-- Unit Level
SELECT
unit_id,
group_id,
if(passes_filter, 1, 0) as value
FROM (
SELECT
source_data.*,
exposures_data.group_id,
source_data.date = MAX(source_data.date) over (partition by source_data.unit_id) as is_latest_date
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
)
WHERE is_latest_date = 1
GROUP BY
source_data.unit_id,
exposure_data.group_id;
-- Experiment
SELECT
group_id,
COUNT(distinct unit_id) total_units
FROM exposure_data
GROUP BY group_id;
-- Group Level
SELECT
group_id,
SUM(value)/SUM(total_units) as mean
FROM unit_data
JOIN group_data
USING (group_id)
GROUP BY group_id;
```
## Options
* Metric Breakdowns: configure Metadata Columns to group results by for dimensional views in pulse results.
* Multi Source: build metrics from multiple metric sources, equivalent to a UNION in SQL. Useful when the same measure exists in sharded or disparate tables.
* Rollup Mode: controls the specific way that Unit Count metrics are aggregated.
================================================================================
# Unit Count (Daily Participation) Metrics
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/unit-count-rate
Updated: 2026-07-11
================================================================================
## Use cases
This metric type is an analogue for DAU. It is useful for growth accounting and for measuring ongoing usage and retention.
## Calculation
At the unit level, unit count metrics create a 1/0 flag for if they participated on a given day.
At the group level, Statsig calculates the mean as the SUM of the unit-level flags divided by each unit's days in the experiment, then divided by the count of UNIQUE UNITS exposed to the experiment.
The SQL for this calculation:
```sql expandable
-- Unit Level
SELECT distinct
source_data.unit_id,
exposure_data.group_id
COUNT(distinct source_data.date) as value
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
GROUP BY
source_data.unit_id,
exposure_data.group_id;
-- Experiment
SELECT
group_id,
unit_id,
date_diff(first_exposure_ds, today()) + 1 AS days_exposed
FROM exposure_data
GROUP BY group_id;
-- Group Level
SELECT
group_id,
SUM(value/days_exposed)/COUNT(distinct unit_id) as mean
FROM unit_data
JOIN group_data
USING (group_id, unit_id)
GROUP BY group_id;
```
## Options
* Metric Breakdowns: configure Metadata Columns to group results by for dimensional views in pulse results.
* Multi Source: build metrics from multiple metric sources, equivalent to a UNION in SQL. Useful when the same measure exists in sharded or disparate tables.
* Rollup Mode: controls the specific way that Unit Count metrics are aggregated.
================================================================================
# Mean Metrics
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/mean
Updated: 2026-07-11
================================================================================
### Use cases
Mean metrics are most often used on event-level data, for example to measure "Average Time to Load" or "Average Purchase Value".
## Calculation
At the unit level, mean metrics SUM their value column, and COUNT records where the value column is non-null.
At the group level, Statsig calculates the mean as the SUM of the unit-level sums, and the SUM of the unit-level counts.
The SQL looks like the following:
```sql expandable
-- Unit Level
SELECT
source_data.unit_id,
exposure_data.group_id,
SUM(source_data.value_column) as value,
COUNT(source_data.value_column) as records
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
WHERE value_column IS NOT NULL
GROUP BY
source_data.unit_id,
exposure_data.group_id;
-- Group Level
SELECT
group_id,
SUM(value)/SUM(records) as mean
FROM unit_data
GROUP BY group_id;
```
### Methodology notes
Internally, mean metrics function like a SUM/COUNT [Ratio](./ratio) metric.
Mean metrics have the delta method applied to account for covariance between unit-level numerators and denominators.
## Options
* Metric Breakdowns
* You can configure Metadata Columns to group results by, getting easy access to dimensional views in pulse results
* Winsorization
* Specify a lower and/or upper percentile bound to winsorize at. Statsig clamps all values below the lower threshold, or above the upper threshold, to that threshold to reduce the outsized impact of outliers on your analysis
* [Cohort Windows](/statsig-warehouse-native/features/cohort-metrics)
* You can specify a window for data collection after a unit's exposure. For example, a 0-1 day cohort window would only count actions from days 0 and 1 after a unit was exposed to an experiment
* Select **Only include units with a completed window** to remove units from pulse analysis for this metric until the cohort window has completed
* [Baked Metrics](/statsig-warehouse-native/features/cohort-metrics#metric-bake-windows)
* [Baked Metrics](/statsig-warehouse-native/features/cohort-metrics#metric-bake-windows) let you specify how long a metric needs to mature. This is common in situations like chargebacks or cancellations. Statsig delays loading the data until the window has elapsed, and only calculates pulse results for that metric after a unit's metric has matured.
================================================================================
# Ratio Metrics
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/ratio
Updated: 2026-07-11
================================================================================
## Use cases
Ratio metrics provide more nuanced understanding: revenue went up and purchase volume increased, but did revenue per purchase increase in a meaningful way?
Ratio metrics also normalize metrics. For example, if you rent out devices like scooters, you might run a scooter-level experiment but want to measure average revenue per distinct rider. You can create a metric of SUM(revenue)/COUNT\_DISTINCT(rider\_id) to calculate this normalized metric and reduce the influence of scooters in popular areas that get many riders.
## Calculation
At the unit level, ratio metrics calculate the unit-level aggregation of both component metrics.
At the group level, the mean is the total group value of the first metric divided by the total group value of the second metric.
{% callout type="note" %}
The denominator is not the number of units in the experiment; the normalization is by the denominator metric.
{% /callout %}
The SQL for this calculation:
```sql expandable
-- Denominator (Checkouts)
SELECT
source_data.unit_id,
exposure_data.group_id,
COUNT(1) as denominator
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
GROUP BY
source_data.unit_id,
exposure_data.group_id;
-- Numerator (Revenue)
SELECT
unit_id,
group_id,
SUM(revenue) as numerator
FROM source_data
GROUP BY unit_id, group_id;
-- Group Level
SELECT
group_id,
SUM(numerator)/SUM(denominator) as mean
FROM denominator
-- full outer join depending on settings
LEFT JOIN numerator
USING (group_id)
GROUP BY group_id;
```
### Methodology notes
Ratio metrics require adjustment because the numerator and denominator may covary at the unit level. Statsig uses the delta method to estimate this adjustment.
By default, Statsig treats ratio metrics as a conversion rate (unordered), counting numerator events only for units that also performed the denominator event. In Statsig Warehouse Native, you can configure this behavior through Advanced settings at the metric level.
## Options
* Treat as conversion rate (unordered): includes numerator events only if the unit also performed the denominator event, regardless of order. Uncheck for a simple ratio, which counts numerator events for all units even if they never performed the denominator event.
* [Cohort Windows](/statsig-warehouse-native/features/cohort-metrics) (Numerator and Denominator): specify a window for data collection after a unit's exposure. For example, a 0-1 day cohort window counts only actions from days 0 and 1 after exposure. Select **Only include units with a completed window** to exclude units from pulse analysis until their cohort window has completed.
* Winsorization: specify a lower and/or upper percentile bound. You can set thresholds independently for the numerator and denominator. All values below the lower threshold or above the upper threshold are clamped to reduce the impact of outliers.
* [Baked Metrics](/statsig-warehouse-native/features/cohort-metrics#metric-bake-windows): specify how long a metric needs to mature. This is common for chargebacks or cancellations. Statsig delays loading the data until the window has elapsed, and calculates pulse results only after a unit's metric has matured.
================================================================================
# Funnel++
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/funnel
Updated: 2026-07-11
================================================================================
{% callout type="info" %}
Funnel++ is a Statsig Warehouse Native feature.
{% /callout %}
## How funnel metrics work
Funnel metrics measure user journeys through a series of steps. For configuration guidance, refer to [funnel metrics](/statsig-warehouse-native/features/funnel-metrics).
Statsig provides advanced capabilities within the statistical framework used in pulse analysis. These include:
* Configurable completion windows per-step
* Session controls - going beyond user-based conversion
* Built-in allowance for timestamp noise
### Use cases
Use funnel metrics to understand how users move through your product. For example, you might want to measure:
* User conversion through a subscription flow, for example Start -> Description Page -> Payment Info -> Confirm, to measure if an experiment causes users to drop off less, and at which steps.
* User conversion from "logged out visitor" to "first logged-in-article-read" using Statsig's [ID Resolution](/statsig-warehouse-native/features/funnel-metrics).
* Session-level conversion for checking out a vacation property, to understand conversion for every unique checkout flow, not only at the user level.
## Calculation
At the unit level, funnel metrics calculate, for each step of the funnel, if the unit completed that step some time after all previous steps were completed in order. This creates a series of step flags.
If using session funnels, those step flags are instead counts of unique sessions.
At the group level, Statsig calculates the stepwise mean as the units for the next step divided by the units for the current step. Statsig calculates the overall mean as the units/sessions that completed the funnel divided by the unit/sessions that started the funnel.
{% callout type="note" %}
For each step, the *first* occurrence after the previous step is treated as the canonical trigger and timestamp for that event going forward for subsequent timestamp comparisons.
{% /callout %}
The SQL looks like the following:
```sql expandable
-- Unit Level, per step
SELECT distinct
source_data.unit_id,
source_data.funnel_session_id, -- optional
source_data.funnel_step_id,
IF(``, 1, 0) as numerator,
IF(``, 1, 0) as denominator
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
;
--Group Level
SELECT
group_id,
funnel_step_id,
SUM(numerator)/SUM(denominator) as mean
FROM unit_data
GROUP BY group_id;
```
### Methodology notes
Conversion metrics require adjustment due to potential unit-level covariance between the numerator and the denominator. Statsig uses the delta method to estimate this adjustment.
By default, Statsig only includes numerators from metrics with non-null, non-zero denominators. This is configurable in the advanced settings.
Funnels in experiment-analysis order strictly. For example, in the funnel A->B->C, all subsequent timestamp comparisons are based on the FIRST occurrence of A. If a user has an A event on day 0, with no other events, and then A/B/C all occur in order on day 5, this funnel won't count as completed if there is a 1-day conversion window from A->B since the time from the first A to the first B is 5 days.
## Options
* Conversion window
* A step-level setting specifying how long this step has to occur after the previous step. For example, in the funnel A->B->C, if B has a conversion window of one hour, Statsig only counts it if it occurs within 1 hour of A.
* Use Strict Event Ordering
* Whether to use >= or > when comparing step timestamps. Strict mode allows you to have two subsequent steps of the same event without it "automatically" passing
* Timestamp Allowance
* Allow some amount of buffer, in milliseconds, between funnel steps. Event logs can have noise on timestamps, so sometimes events may be logged slightly out of order. This setting helps to correct for this issue.
* Count Distinct Mode
* Whether to count sessions or units. For sessions, you must provide a session identifier field on each step
* Calculation window (optional)
* How long a unit has to complete the funnel after starting, and whether the funnel starts when the unit is exposed to the experiment or when they trigger the first event in the funnel
* Treat Exposure as Initial Funnel Event
* With this setting enabled, the first step of the funnel is the exposure event of the experiment. This makes it easy to measure the conversion rate to the first event, and additionally normalizes the final outcome per experiment-user. This setting is incompatible with session-based funnels.
* Measure time to complete
* Switches the funnel mode into measuring the average time for users to complete a funnel. This creates a ratio metric, where the numerator is the sum of funnel seconds-to-complete, and the denominator is the number of completed funnels. This can be useful in isolation, or when paired with a conversion measurement to understand the change to both completion rate and time to complete.
## Visualization
In addition to breaking down lift by funnel steps, you can create funnel visualizations by step.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
================================================================================
# Percentile Metrics
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/percentile
Updated: 2026-07-11
================================================================================
### Use cases
The mean of a metric can be acceptable while the tail end (for example, the 99th percentile) is not. Consider a website with an average TTL of 300ms but a p99 TTL of 1 minute. A small portion of the population has an unusable experience that doesn't appear in the mean, making it difficult to measure whether the p99 value changed with traditional A/B/n metrics.
Percentile metrics are common as guardrail metrics for performance regression, and for measuring improvements from investments in performance and infrastructure.
## Calculation
For percentile metrics, there is no unit-level calculation; Statsig runs the analysis at the group level.
The SQL looks like the following:
```sql expandable
-- Group Level
SELECT
exposures_data.group_id,
PERCENTILE(user_data.value, percentile_level) as value,
COUNT(distinct user_data.user_id) as population
FROM user_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
WHERE value IS NOT NULL
GROUP BY group_id;
```
### Methodology notes
Percentile metrics use the outer CI method to estimate a confidence interval and significance. Deng et al. describe the methodology in section 4 of [this paper](https://arxiv.org/pdf/1803.06336).
{% callout type="note" %}
Some metrics aren't well-formed for this approach, because the method assumes the underlying distribution is continuous. For example, if your data has 1/3 of rows with a value of 0, 1/3 with a value of 5, and 1/3 with a value of 10, Statsig doesn't calculate significance for a median or p99 metric because there is no local variability.
{% /callout %}
## Options
* Metric Breakdowns
* You can configure Metadata Columns to group results by, getting easy access to dimensional views in pulse results
================================================================================
# Log Metrics
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/log
Updated: 2026-07-11
================================================================================
### Use cases
Log metrics are useful for understanding if the distribution of a log-normal or tail-driven metric has shifted. Statsig calculates the metric as a conditional mean: a ratio metric where the numerator is the sum of unit-level log values and the denominator is 1 for units with a valid log. Statsig filters out records with a 0 denominator, because imputing 0s for logs doesn't work without a treatment such as an inverse hyperbolic sine function.
Common uses include revenue, time spent, or other metrics where a small portion of users drives most of the value but bulk improvements matter. Log metrics measure relative change per unit: an increase of 1 corresponds to a multiplication by the log's base.
## Calculation
At the unit level, count metrics run a COUNT(1) or SUM(`value`) across their metric source.
At the group level, Statsig calculates the mean as the SUM of the log of the unit-level value, divided by the count of units with a unit-level value that log is valid for (exists, and is greater than 0).
The SQL for a count metric looks like the following:
```sql expandable
-- Unit Level
SELECT
source_data.unit_id,
exposure_data.group_id,
COUNT(1) as value
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
GROUP BY unit_id, group_id;
-- Group Level
SELECT
group_id,
-- divide the sum of the logged values by the count of participating units
SUM(LOG(value, ))/COUNT(1) as mean
FROM unit_data
WHERE value > 0
-- the filter is implicit from the CTE, but let's make it explicit
-- a sum metric might have negative values
GROUP BY group_id;
```
### Methodology notes
Log metrics can be difficult to interpret and to extrapolate to topline values. Use log metrics together with the raw or winsorized SUM and COUNT metric.
There are a few ways to handle 0s in a log metric. A transformation like [IHS](https://en.wikipedia.org/wiki/Inverse_hyperbolic_functions) can approximate the behavior of log for large values while accepting 0s as inputs. Alternatively, you can scope the analysis to non-zero units. Statsig uses the second approach for ease of interpretation, because log properties are broadly understood.
This means there is a potential confounding factor of participation rate. To mitigate this, Statsig presents results the same as for [ratio](/statsig-warehouse-native/metrics/ratio) metrics, including statistics for the overall result and the implicit numerators and denominators.
## Options
Non-log options depend on whether the metric is a Sum or Count.
* Custom log Base
* You can configure a custom base for the log operation. Defaults to LN
================================================================================
# First or Latest Value Metrics
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/latest-value
Updated: 2026-07-11
================================================================================
## Use cases
### First Value
First value metrics track the initial experience a user has with your product. This could be first purchase value, initial time-to-load, or any other initial result you are expecting to influence with your change.
### Latest Value
Latest value metrics track how an experiment is impacting the state of your population. For example, you might want to measure if the test group has a higher net account balance, or if the current loyalty rewards balance is higher for users in one arm of an experiment.
You can use latest value metrics to approximate user statuses, such as "is\_subscriber", if you have a Metric Source with a 1/0 flag for all users. Statsig recommends the [unit count](./unit-count-latest) equivalent for this use case, because you can filter on a sparse dataset and Statsig handles imputing and tracking 0s.
## Calculation
At the unit level, first value metrics calculate each day's first non-null value within cohort bounds. Similarly, latest value metrics calculate each day's latest non-null value within any cohort bounds.
For pulse, Statsig carries the first value forward from the first date with data, and determines the latest value by taking the latest value from the latest day available.
At the group level, Statsig calculates the mean as the SUM of the unit-level values, divided by the count of UNIQUE UNITS exposed to the experiment.
The SQL looks like the following:
```sql expandable
-- Unit Level
SELECT
source_data.unit_id,
exposure_data.group_id,
LATEST_VALUE(source_data.value_field) as value
-- or FIRST_VALUE
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
WHERE value_field IS NOT NULL
GROUP BY
source_data.unit_id,
exposure_data.group_id;
-- Experiment
SELECT
group_id,
COUNT(distinct unit_id) total_units
FROM exposure_data
GROUP BY group_id;
-- Group Level
SELECT
group_id,
SUM(value)/SUM(total_units) as mean
FROM unit_data
JOIN group_data
USING (group_id)
GROUP BY group_id;
```
### Methodology notes
Statsig treats users without a value as 0s. If an existing value is present, the user retains that value unless a 0 or a new value is provided later.
## Options
* Metric Breakdowns
* You can configure Metadata Columns to group results by, getting easy access to dimensional views in pulse results
* [Cohort Windows](/statsig-warehouse-native/features/cohort-metrics)
* You can specify a window for data collection after a unit's exposure. For example, a 0-1 day cohort window would only count actions from days 0 and 1 after a unit was exposed to an experiment
* Select **Only include units with a completed window** to remove units from pulse analysis for this metric until the cohort window has completed
* [CUPED](/experiments/statistical-methods/methodologies/cuped)
* Specify if you want to calculate CUPED, and the lookback window for CUPED's pre-experiment data inputs
### Special Case: Surrogate Metrics
You can use latest value metrics to implement surrogate metrics. Surrogate metrics (aka proxy metrics or predictive metrics) are a prediction of some long term metric that's impractical to measure over the duration of an experiment, and have some inherent prediction error associated with the model used to derive the metric values.
In advanced settings for latest value metrics, you can indicate a metric as a surrogate metric with a mean squared error (MSE). Prediction accuracy is then accounted for in variance calculation, which adjusts p-values and confidence intervals accordingly.
Consider the variable X to be the true north metric which is being predicted by the surrogate metric S. The surrogate metric S is assumed to be an unbiased estimator with an error term $\epsilon$.
$$
\mu\_\{X} = \mu\_\{S} = \overline\{S}
$$
$$
Var(X) = Var(S + \epsilon) = Var(S) + MSE
$$
$$
Var(\overline\{X}) = \frac\{Var(X)}\{n} = \frac\{Var(S) + MSE}\{n}
$$
================================================================================
# Retention Metrics
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/retention
Updated: 2026-07-11
================================================================================
## Use cases
Retention metrics measure user stickiness, conversion, and growth over the duration of experiments and holdouts. For example, a retention metric can evaluate changes in "Current User Retention", "Notification Retention", or "Video Viewer Retention" over an experiment, broken down in timeseries and days-since-exposure views.
Many platforms limit retention metrics to checking if a unit was active between days X and X+Y since exposure. This is useful for new-user or marketing experiments but is less useful for experiments targeted at an existing user base.
[This article in Lenny's newsletter](https://www.lennysnewsletter.com/p/how-duolingo-reignited-user-growth) shows how teams use these metrics to drive user growth.
Use this metric type for any change aimed at increasing user stickiness, such as changes to notifications, reactivation campaigns, or quality improvements.
## Setup and definition
Retention metrics are defined with a duration and a lookback window. The period is measured backwards from the end. For example, "Lookback = 7, Duration = 14" (L7D14) measures the week ending 14 days after the start event.
{% figure %}

{% /figure %}
This is a rolling calculation. Each day a user triggers the start event, the metric denominator increments by 1. If the user is active in the corresponding completion window, the numerator is 1 for that day.
Statsig includes only days with completed windows in pulse. For example, if the duration is 7, Statsig excludes the last week of data from pulse to avoid diluting the metric, because an L3D7 metric would always have a numerator of 0 for those days.
The `allow cohort metrics to mature after experiment end` setting in advanced experiment settings allows post-experiment data to complete the analysis, so Statsig includes units exposed later in the experiment. Use this setting when the treatment is one-time and doesn't need to be re-applied to affect users.
{% figure %}

{% /figure %}
## Calculation
```sql expandable
-- Denominator - 1/0 flag for activity on a day
WITH denominator AS (
SELECT
source_data.unit_id,
source_data.date,
exposure_data.group_id,
MAX(1) as denominator
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
WHERE
GROUP BY ALL;
),
-- Numerator Candidates - 1/0 flag for success activity on a day
-- Note by default this is equivalent to the denominator CTE
numerator_candidates AS (
SELECT
source_data.unit_id,
source_data.date,
exposure_data.group_id,
MAX(1) as denominator
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
WHERE
GROUP BY ALL
),
-- Numerators, deduplicated - 1/0 flag for success per denominator
-- Now we have a 1-0 numerator flag per denominator-day
joined_data AS (
SELECT
denominator.unit_id,
denominator.date,
den.group_id,
1 as denominator
MAX(CASE WHEN numerator_candidates.unit_id IS NOT NULL THEN 1 ELSE 0 END) as numerator
FROM denominator
LEFT JOIN numerator_candidates
ON denominator.unit_id = numerator_candidates.unit_id
AND numerator_candidates.date BETWEEN
denominator.date + INTERVAL '' DAY
AND denominator.date + INTERVAL '' DAY
GROUP BY ALL
)
-- Group Level
SELECT
group_id,
SUM(denominator) as unit_days_started,
SUM(numerator) as unit_days_completed,
SUM(numerator)/SUM(denominator) as mean
FROM joined_data
GROUP BY ALL
```
### Methodology notes
Statsig treats retention metrics as [ratio metrics](/statsig-warehouse-native/metrics/ratio) for pulse calculations. The only distinction is that the metric date is attributed to the denominator date.
The ratio components for retention metrics reflect the rolling metric definition:
* the denominator is the average number of days per user where the "retention start" event was triggered
* the numerator is the average number of days per user where a "retention start" event had a corresponding "retention end" event in its retention period.
## Options
* Metric Breakdowns
* You can configure Metadata Columns to group results by, getting easy access to dimensional views in pulse results
* Retention Lookback Window (Days)
* The length of the "Completion Event" collection window
* Retention Period End (Days)
* When to stop measuring retention completion events
* Use a different start and completion event for retention calculations: choose a secondary event for completion windows. By default, retention measures a behavior's retention against itself. Enabling this option lets you measure a secondary event instead. For example, with user accounting flags, you can measure "IS CHURNED" -> "IS REACTIVATED" and "IS REACTIVATED" -> "IS CHURNED" to track both reactivation and falloff of a long-term marketing test.
================================================================================
# Max/Min Metrics
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/max-min
Updated: 2026-07-11
================================================================================
### Use cases
Max/min metrics track users' extremes during an experiment.
Common examples are:
* Measuring the impact of performance changes on users' worst experiences by analyzing the maximum latency per user.
* Measuring the effect of game changes on user performance by calculating the peak high score per user.
* Counting the number of users who ever left a 2-star review or lower by applying MIN(review\_score) with a threshold condition.
## Calculation for max/min
At the unit level, max/min metrics take the max/min of their input column.
At the group level, Statsig calculates the max/min as the MEAN of the unit-level sums, divided by the count of UNIQUE UNITS exposed to the experiment.
The MAX would look like the SQL below:
```sql expandable
-- Unit Level
SELECT
source_data.unit_id,
exposure_data.group_id,
MAX(source_data.value_column) as value
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
GROUP BY
source_data.unit_id,
exposure_data.group_id;
-- Experiment
SELECT
group_id,
COUNT(distinct unit_id) total_units
FROM exposure_data
GROUP BY group_id;
-- Group Level
SELECT
group_id,
SUM(value)/SUM(total_units) as mean
FROM unit_data
JOIN group_data
USING (group_id)
GROUP BY group_id;
```
The MIN would look like the SQL below:
```sql expandable
-- Unit Level
SELECT
source_data.unit_id,
exposure_data.group_id,
MIN(source_data.value_column) as value
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
GROUP BY
source_data.unit_id,
exposure_data.group_id;
-- Experiment
SELECT
group_id,
COUNT(distinct unit_id) total_units
FROM exposure_data
GROUP BY group_id;
-- Group Level
SELECT
group_id,
SUM(value)/SUM(total_units) as mean
FROM unit_data
JOIN group_data
USING (group_id)
GROUP BY group_id;
```
### Methodology notes
Max/min metrics are simple to use, and many advanced options are available.
## Options
* Metric Breakdowns
* You can configure Metadata Columns to group results by, getting easy access to dimensional views in pulse results
* Winsorization
* Specify a lower and/or upper percentile bound to winsorize at. Statsig clamps all values below the lower threshold, or above the upper threshold, to that threshold to reduce the outsized impact of outliers on your analysis
* CUPED
* Specify if you want to calculate CUPED, and the lookback window for CUPED's pre-experiment data inputs
* Thresholding
* Turn this metric into a 1/0 unit count metric counting if the unit's max/min equals to or surpasses (>=) a given threshold
* [Cohort Windows](/statsig-warehouse-native/features/cohort-metrics)
* You can specify a window for data collection after a unit's exposure. For example, a 0-1 day cohort window would only count actions from days 0 and 1 after a unit was exposed to an experiment
* Select **Only include units with a completed window** to remove units from pulse analysis for this metric until the cohort window has completed
* [Baked Metrics](/statsig-warehouse-native/features/cohort-metrics#metric-bake-windows)
* [Baked Metrics](/statsig-warehouse-native/features/cohort-metrics#metric-bake-windows) let you specify how long a metric needs to mature. This is common in situations like chargebacks or cancellations. Statsig delays loading the data until the window has elapsed, and only calculates pulse results for that metric after a unit's metric has matured.
================================================================================
# Composite Metrics
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/composite
Updated: 2026-07-11
================================================================================
### Use cases
Composite metrics are a flexible metric type designed to sum aggregated metric sources at the user level. Use them when you need to add or subtract two or more aggregated values into a single signal, such as:
* Net value (e.g., revenue minus refunds)
* Unit-level experiment change (latestvalue minus first value)
## Calculation
At the unit level, composite metrics first compute the aggregations of each component. Statsig then adds or subtracts the aggregated results according to the formula specified (for example: `A + B` or `A - B + C`).
Statsig performs this calculation at a daily, 7-day, and cumulative level during experiment analysis.
{% callout type="note" %}
Composite metrics don't currently support daily rollups in turbo mode.
{% /callout %}
At the group level, Statsig calculates the mean as the average of the unit-level composite aggregation.
### Example
If you define a composite gap metric as `Max Price - Min Price`, Statsig computes `Max Price` and `Min Price` for each user, then subtracts to get each user's net value. The experiment group mean is the average of those per-user results.
The SQL for the individual components would look like the following:
MAX Component:
```sql expandable
-- Unit Level
SELECT
source_data.unit_id,
exposure_data.group_id,
MAX(source_data.value_column) as value
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
GROUP BY
source_data.unit_id,
exposure_data.group_id;
-- Experiment
SELECT
group_id,
COUNT(distinct unit_id) total_units
FROM exposure_data
GROUP BY group_id;
-- Group Level
SELECT
group_id,
SUM(value)/SUM(total_units) as mean
FROM unit_data
JOIN group_data
USING (group_id)
GROUP BY group_id;
```
MIN Component:
```sql expandable
-- Unit Level
SELECT
source_data.unit_id,
exposure_data.group_id,
MIN(source_data.value_column) as value
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
GROUP BY
source_data.unit_id,
exposure_data.group_id;
-- Experiment
SELECT
group_id,
COUNT(distinct unit_id) total_units
FROM exposure_data
GROUP BY group_id;
-- Group Level
SELECT
group_id,
SUM(value)/SUM(total_units) as mean
FROM unit_data
JOIN group_data
USING (group_id)
GROUP BY group_id;
```
Composite Aggregation:
```sql expandable
-- Unit Level
SELECT
source_data.unit_id,
exposure_data.group_id,
MAX(source_data.value_column) - MIN(source_data.value_column) AS value
FROM source_data
JOIN exposure_data
ON
-- Only include users who saw the experiment
source_data.unit_id = exposure_data.unit_id
-- Only include data from after the user saw the experiment
-- In this case exposure_data is already deduped to the "first exposure"
AND source_data.timestamp >= exposure_data.timestamp
GROUP BY
source_data.unit_id,
exposure_data.group_id;
-- Experiment
SELECT
group_id,
COUNT(distinct unit_id) total_units
FROM exposure_data
GROUP BY group_id;
-- Group Level
SELECT
group_id,
SUM(value)/SUM(total_units) as mean
FROM unit_data
JOIN group_data
USING (group_id)
GROUP BY group_id;
```
## Options
* [Cohort Windows](/statsig-warehouse-native/features/cohort-metrics)
* You can specify a window for data collection after a unit's exposure. For example, a 0-1 day cohort window would only count actions from days 0 and 1 after a unit was exposed to an experiment
* Select **Only include units with a completed window** to remove units from pulse analysis for this metric until the cohort window has completed
* Winsorization
* Specify a lower and/or upper percentile bound to winsorize at. Statsig applies these bounds separately to the top-level metric *and* its individual components.
* [Baked Metrics](/statsig-warehouse-native/features/cohort-metrics#metric-bake-windows)
* [Baked Metrics](/statsig-warehouse-native/features/cohort-metrics#metric-bake-windows) let you specify how long a metric needs to mature. This is common in situations like chargebacks or cancellations. Statsig delays loading the data until the window has elapsed, and only calculates pulse results for that metric after the unit's metric has matured.
================================================================================
# Metric Examples
URL: https://docs.statsig.com/statsig-warehouse-native/configuration/metric-examples
Updated: 2026-07-11
================================================================================
For customers transitioning from other warehouse-native vendors, the metric format is similar. Customers have generally been able to use APIs to fetch remote configurations, translate them, and post them to Statsig without issue.
For customers migrating from in-house systems, there may be differences between how experiment metrics are defined internally and how Statsig handles them. This page covers common use cases and how Statsig handles them.
## Average user revenue from a wide table
In many cases, companies have a primary source-of-truth table about user engagement with one row per user-day and many columns representing actions taken or other values. This table type integrates directly with Statsig.
First, enter the table path and optionally a partition column to use for date partitioning:
{% figure %}

{% /figure %}
Then, configure your timestamp field and ID types. Add any custom SQL aliases, for example, dividing revenue by 100 to convert from cents to dollars.
{% figure %}

{% /figure %}
Go to the metrics tab, press create, configure your name/source, and then configure a sum metric on the column with the revenue value.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
### How it works in experiments
First, Statsig aggregates each unit-level record across the days they are enrolled in the experiment.
Then, Statsig calculates the mean unit-level revenue per experiment group, imputing 0s for all exposed users with no revenue.
Statsig provides a description of this in-product for any user who wants to learn more:
{% figure %}

{% /figure %}
## Average current account value
Often, you will want to understand if your experiment has altered the state of users. For example, you may want to know whether users in test vs. control have grown their account value.
To measure this, provide a table or query that tracks users' account values each day. Then, set up a metric source pointing to that table or query.
Go to the metrics tab, press create, configure your metric name & source, and then configure a latest value metric on the column with the account value.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
### How it works in experiments
First, at unit level, Statsig calculates each day's latest non-null value within any cohort bounds and takes the latest value from the latest day available.
Then, Statsig calculates the mean unit-level value per experiment group on each day, imputing 0s for all exposed users with no value.
Statsig provides a description of this in-product for any user who wants to learn more:
{% figure %}

{% /figure %}
## Users' D7 participation rate
A common metric in experimentation is measuring whether exposed users take specific actions within a defined time window.
Provide an event table that records user actions with essential columns such as user\_id, timestamp, and event type. Configure your timestamp field and ID types as described in the previous sections.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
Navigate to the metric catalog and create a unit count metric using the defined metric source. Use the 'Add Filter' option to focus on specific events relevant to your metric.
{% figure %}

{% /figure %}
When defining the metric, you can choose from several rollup modes:
* Daily Participation Rate: measures the days a unit was active after exposure, divided by its total days in the experiment
* On-Time Event: measures whether a unit performed an action any time after exposure
* Latest Value: measures whether a unit passed metric filters on their last observed record
* Custom Attribution Window: includes data for each unit within a specified time window after exposure
{% figure %}

{% /figure %}
To measure user participation within 7 days, select 'Custom Attribution Window' as your rollup mode and set start = 0, end = 6 to define a 7-day window. Enable 'Only include units with a completed window' to exclude users who haven't yet reached the full 7-day period from your analysis.
{% figure %}

{% /figure %}
### How it works in experiments
First, at unit level, Statsig creates a 0/1 flag if the event triggers during the specified time window.
Then, at the group level, Statsig calculates the mean as the SUM of the unit-level flags, divided by the count of UNIQUE UNITS exposed to the experiment.
Refer to [Unit Count (Window) Metrics](/statsig-warehouse-native/metrics/unit-count-window) for more details about how these metrics are calculated.
## User funnel metric
A common analysis in experimentation is understanding how a new feature impacts dropoff rates at each step of a user funnel.
To create a funnel metric in Statsig, you need an event table that records each step of the events you want to track. The setup for your metric source follows the same process described in previous sections.
Navigate to the metric catalog, and select 'Funnel' as your metric type. Choose the unit level for your funnel steps: a distinct count of users or sessions, depending on what you want to measure.
{% figure %}

{% /figure %}
Then, define your funnel steps, specifying the sequence of events users go through.
{% figure %}

{% /figure %}
In the Advanced Settings, you can further customize your funnel metric. Options include specifying a calculation window, measuring time to convert, and treating exposure as the initial funnel event.
{% figure %}

{% /figure %}
### How it works in experiments
First, at unit level, Statsig constructs a 1/0 (or session-count number for session funnels) metric for each step of the funnel. This flag is 1 if the unit completed that step some time after all previous steps were completed in order. If using a session-level funnel, it's the number of sessions where that is true, e.g. all previous steps were completed in order for that session key.
Then, at the group level, Statsig calculates the stepwise mean as the total of each step's metric divided by the total metric from the previous step. Statsig calculates the overall mean as the units/sessions that completed the funnel divided by the unit/sessions that started the funnel.
Statsig provides a description of this in-product for any user who wants to learn more:
{% figure %}

{% /figure %}
## User retention rate
A retention metric measures changes in user stickiness and product growth.
To create a retention metric in Statsig, you need an event table that captures the key activities indicating user retention. The setup for your metric source follows the same process described in previous sections.
Navigate to the metric catalog and select 'Retention' as your metric type. Configure the retention period and lookback window. For example, if you set 'Retention Period End' to 14 and the retention lookback window to 7, retention is measured as whether the user triggered the retention event between day 8 and day 14.
{% figure %}

{% /figure %}
You can enable "Use a different start and completion event for retention calculations" if you want to use a different starting event than exposure, or if you want to define a specific subset of events as your retention event.
For example, based on the setup shown in the screenshots, this measures the week 2 retention rate of users who made a purchase in week 1.
{% figure %}

{% /figure %}
In the Advanced Settings, you can configure the ID type for your retention metric.
{% figure %}

{% /figure %}
### How it works in experiments
First, for each unit per day, Statsig checks if the retention start event triggers and assigns a 0/1 flag, which serves as the denominator of the calculation.
Next, Statsig checks if the retention completion event occurs within the specified time window and assigns a 0/1 flag, which serves as the numerator of the calculation.
Finally, at the group level, Statsig calculates retention as sum(numerator) / sum(denominator) to determine the overall retention rate.
Statsig provides a description of this in-product for any user who wants to learn more:
{% figure %}

{% /figure %}
================================================================================
# Cohort Metrics
URL: https://docs.statsig.com/statsig-warehouse-native/features/cohort-metrics
Updated: 2026-07-11
================================================================================
Cohort metrics are useful for many reasons. Common use cases are:
* By ensuring all users have equal periods for data collection, there is an "apples to apples" comparison across user enrolled early/late in the experiment (which often corresponds to power/occasional users), and across different "time periods" that may have extrinsic factors like holidays
* If analyzing an unbounded period, experimental units' variance in the population can increase over time - leading to scenario where error bars don't actually converge towards 0 as the experiment is run for longer!
* This allows one to skip noisy early metrics, or focus on outcomes after users might churn - e.g. capturing "week-2 engagement" if a product has a 1-week trial period
* This can also be used to capture "one-shot retention". [Retention metrics](/statsig-warehouse-native/metrics/retention) are used to capture rolling, ongoing retention. A user metric with a window from day X to day Y is a good way to check if an experiment is causing more users to retain at least X days
The downsides of cohort metrics are that:
* They don't capture any sort of long-term impact, or how that evolves over time. This is purely a point in time analysis and may not be appropriate for measuring complex, evolving behaviors
* They make topline impact estimates lossier and harder to trust
Some practitioners have made compelling arguments that cohort metrics are a better "standard" metric for organizations to use in analysis. Statsig tends to believe that the use of cohorts is dependent on business context, but consider whether they should be at least a part of an experiment's measurement (for example, measuring topline revenue as an overall evaluation criteria, but also measuring 7d revenue alongside it for additional context).
This page explains the available settings, what they do, and how they interact so you know what Statsig measures.
## Basic cohort windows
Basic cohort windows are a filter on metric data with a time range relative to the unit's time of exposure.
For example, a cohort window from day 1 to day 6 filters to events from 24 hours until 168 hours after exposure.
{% callout type="note" %}
This is calculated as a timestamp comparison; a unit enrolled at 12pm will have exactly 24 hours until they hit the end of a 0-1 day cohort.
{% /callout %}
For metrics from data sources that are marked as daily data, the cohort comparison is truncated to a date so that day-0 data behaves as expected (e.g. a user exposed on `2025-01-05T09:00` will include the date-based data from `2025-01-05` instead of truncating to times "after 9:00am").
## Waiting for maturation
By default, cohort metrics can have a mix of maturation levels in the experimental population. For a 1-week cohort, users enrolled in the final week have a mix of maturities during analysis. This yields the maximum sample but can dilute the analysis with partial cohort windows. To prevent this, mark the metric as "Wait for cohort window to complete". This drops units' metric data from analysis and removes them from the experiment analysis population.
In the examples below, one metric forces cohorts to complete. It has **less units** in the analysis, since many units don't have a complete window, a **lower total** because of the small unit count, but a **higher mean** since the units it does have have completed their window and have a longer data collection period on average.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
This setting can lead to different populations between metrics and filters out the last few days of an experiment's data in the daily time series, because new cohorts' data is not yet complete. View a metric's cohort settings by hovering over the metric name in the experiment scorecard.
## Visual examples
This is what data collection looks like for a standard cohort metric with a 0-6 day window. This collects data for **7 days** because it is 0-indexed.
{% figure %}

{% /figure %}
If the cohort period extends past the end of the experiment, data collection is truncated to the end of the experiment by default.
{% figure %}

{% /figure %}
If the metric is configured to allow only completed cohort windows, Statsig excludes the unit from the analysis. Statsig doesn't include excluded units in the denominator for the average value of a sum or count metric, and filters their metric data from the analysis.
{% figure %}

{% /figure %}
If mature after end is configured in the experiment, data collection continues after the experiment ends, regardless of whether "wait for mature" is enabled.
{% figure %}

{% /figure %}
## Experiment-based cohort settings
Cohort controls are also available at the experiment level. The relevance of these settings depends on the kind of experiment being run, as described below.
### Allow post-experiment cohort data
Checking **Allow Cohort Metrics to Mature After Experiment End** allows metrics to be collected after the experiment ends. This is recommended for one-time interventions, such as a new signup page, because post-experiment signal from units that received the intervention provides additional statistical power.
{% figure %}

{% /figure %}
This is **not** recommended for continuous interventions, such as a ranking change, because post-experiment data can be diluted. For example, test users may receive the control experience during their post-experiment period, which dilutes results.
The analysis is extended past the experiment end by the length of the longest cohort window across all metrics. Non-cohorted metrics are constrained to the analysis period; cohort metrics are filtered to the experiment end plus their cohort window.
### Fixed duration
On the **Experiment Population** section of the experiment setup page, **Configure Analysis Period** with Analysis Type **Fixed Duration** counts metrics only for a specified period after **experiment start**. This is useful for experiments with a fixed enrollment point, such as email campaigns.
{% figure %}

{% /figure %}
This setting is only available for assign and analyze experiments.
### Cohorted duration
On the **Experiment Population** section of the experiment setup page, **Configure Analysis Period** with Analysis Type **Cohorted Duration** applies allocation-based cohorting globally to all metrics in the experiment analysis. This is the same as the metric-based cohort but applied experiment-wide. This is useful for new user experiments.
{% callout type="note" %}
When this is used in conjunction with metric cohorts, Statsig uses the minimal end of the cohort window.
For example, if a metric cohort is set to end at 7 days and the experiment at 10 days, Statsig uses 7. If the metric cohort is set to end at 7 days and the experiment at 5 days, Statsig uses 5.
{% /callout %}
The **only include units with a completed cohort window** setting can also be specified at the experiment level and applies to all metrics in the experiment when set. If not checked, the setting is applied on a metric-by-metric basis.
## Metric bake windows
In some cases, a metric in your warehouse may not be mature until a certain time has passed, after which you care about the daily value. Statsig provides the option to specify a bake window for your metrics. Statsig excludes metrics that haven't reached the end of their bake window from the numerator and denominator of the metric in the analysis.
Daily revenue is an example of a metric that may not be immediately mature. If a user makes a purchase and refunds it a few days later, their daily revenue retroactively changes to reflect the refund. A 28-day refund period would align naturally with a 28-day bake window for revenue metrics.
{% figure %}

{% /figure %}
In the example above, the revenue metric covers the last 28 days. Rather than calculating a partial result, users are excluded from the analysis until their data has had 28 days to mature.
Partial results can occur because, when a metric bakes over a long period, part of the metric values come from before the experimental intervention and dilute the results. For a user exposed 1 day ago, 27 of 28 days of their revenue metric would be from the pre-intervention period, diluting the experiment results.
================================================================================
# Dimension Analysis
URL: https://docs.statsig.com/statsig-warehouse-native/configuration/dimensional-analysis
Updated: 2026-07-11
================================================================================
{% figure %}

{% /figure %}
You can also configure [Differential Impact Detection](/experiments/exploring-results/differential-impact-detection) to help Statsig automatically flag when different classes of users are responding differently to your experiments.
To view the Pulse result breakdowns for all categories within a metric, select the (+) sign next to the metric.
## Unit dimensions
Unit dimensions refer to unit-level attributes that are either part of the user object you log to Statsig, provide as part of your [assignment data](/statsig-warehouse-native/configuration/assignment-sources), or provide through an [Entity Property](/statsig-warehouse-native/configuration/entity-properties). Examples of these attributes are operating system, country, region, or user segments.
Using [explore queries](/experiments/interpreting-results/custom-queries), you can filter to specific unit dimensions or group results by a dimension. For example, you can view results for users in the US, or view results for users on iOS grouped by their country.
Statsig chooses the dimension based on the last available record at or before exposure. Statsig doesn't use information from after the unit is exposed to a given experiment in the experiment analysis, because using post-exposure data can lead to data leakage and imbalanced comparisons.
## Metric dimensions
Metric dimensions break down a metric's results based on the values in columns from your [metric source](/statsig-warehouse-native/configuration/metric-sources) for a given metric. You configure these breakdowns per-metric, after which Statsig calculates them for that metric across all pulse results.
Unlike Unit Dimensions, Metric Dimensions are not mutually exclusive. For example, with a unit dimension a user can only be from one country for the purpose of pulse analysis, but a user on an e-commerce website could buy "clothes", "books", and "snacks" within a "total purchases" metric and contribute to each of those dimensions as well as the overall value.
## Default behavior
By default, dimensional analysis:
* only considers dimensions with at least 100 units in the experiment that participated (had a non-zero value)
* chooses the top 10 dimensions by total value, and puts all others into an "OTHER" bucket.
These defaults prevent extreme results, avoid cases where centrality assumptions fail due to low sample sizes on a specific dimension, and limit excessive multiple comparisons on the tail end of dimensional breakdowns. This behavior is configurable. Contact Statsig support to evaluate whether your use case warrants a change.
## Precomputed dimension loading timing
For precomputed user dimensions that are configured and run on a schedule, Statsig processes dimension data asynchronously and it may take a few minutes to become available after the main experiment results load. You may temporarily see "No dimensions available for this time range" messages while Statsig processes the data, especially after the first reload of the day. This is expected behavior. Wait a few minutes and refresh the page to see the dimensional breakdowns.
{% callout type="note" %}
This timing behavior only affects precomputed user dimensions. User-triggered dimensional analysis doesn't experience this delay.
{% /callout %}
================================================================================
# Assignment Sources
URL: https://docs.statsig.com/statsig-warehouse-native/configuration/assignment-sources
Updated: 2026-07-11
================================================================================
Assignment Sources define the schema for your assignment data in Statsig. They serve as the input for determining which users are in an experiment and which treatment each user received.
## Creating an assignment source
To create an assignment source, navigate to the data tab in Statsig and open the Assignment Sources pane.
An Assignment Source is defined as a SQL query and a mapping of the output columns to specific fields
Statsig requires (user identifiers, a `timestamp`, an experiment identifier, and a group identifier).
{% figure %}

{% /figure %}
## Scanning assignment sources
Statsig scans assignment sources on-demand or on a schedule to find experiment data. These jobs run quickly and identify unique groups, the ID types present in the experiment, and the estimated number of users per group.
After the scan is complete, you can view and create experiments from the assignment source. The assignment data also populates the Experiment creation flow.
{% figure %}

{% /figure %}
## Manage assignment sources
In the Assignment Source tab, you can see your Assignment sources and the experiments they're being used in.
{% figure %}

{% /figure %}
## Example data
For experiment assignment sources, Statsig requires the following information: which users received an exposure, when, and to which experiment:
| Column Type | Description | Format/Rules |
| ---------------------- | ----------------------------------------------------------------------------------------- | ------------------------------ |
| timestamp | **Required** an identifier of when the experiment exposure occurred | Castable to Timestamp/Date |
| unit identifier | **Required** at least one entity to which this metric belongs | Generally a user ID or similar |
| experiment identifier | **Required** the experiment the exposure was for | Usually an experiment name |
| group identifier | **Required** the experimental variant the user was assigned to | Usually a group name |
| additional identifiers | *Optional* Entity identifiers for reuse across identifier types | |
| context columns | *Optional* Fields which can be used to group by and filter results in exploratory queries | |
For example, you could pull from exposure event logging directly:
| timestamp | user\_id | company\_id | experiment\_name | group\_name | country |
| ------------------- | ------------- | ---------- | ---------------- | ---------- | ------- |
| 2023-10-10 00:01:01 | my\_user\_17503 | c\_22235455 | ranking\_v1\_vs\_v2 | v1 | US |
| 2023-10-10 00:02:15 | my\_user\_18821 | c\_22235455 | ranking\_v1\_vs\_v2 | v2 | CA |
| 2023-10-10 00:02:22 | my\_user\_18821 | c\_22235455 | search UI revamp | control | CA |
================================================================================
# Entity Properties
URL: https://docs.statsig.com/statsig-warehouse-native/configuration/entity-properties
Updated: 2026-07-11
================================================================================
You can provide additional detail about an entity that doesn't typically change (for example, a user's home country), or a property that may change as part of an experiment (for example, Subscriber Status: True/False). For dynamic properties, you provide a timestamp field that Statsig uses to identify the most recent value before the user's exposure. This prevents imbalanced groups and biased results in cases where an experimental treatment impacts the property, for example if the treatment increased the subscription rate.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
## Example data
For property sources, Statsig only needs a user\_id and property fields. Property sources can define **fixed** properties (for example, a user's country of origin), or **dynamic** properties, in which case you need to provide a timestamp for Statsig to identify the most recent pre-exposure record.
| Column Type | Description | Format/Rules |
| ---------------- | ------------------------------------------------------------------------------------------- | ------------------------------ |
| timestamp | *Optional* an identifier of when the property was defined. Required for dynamic properties | Castable to Timestamp/Date |
| unit identifier | **Required** At least one entity to which this metric belongs | Generally a user ID or similar |
| property columns | **Required** Fields which can be used to group by and filter results in exploratory queries | |
For example, a static property source could just be:
| user\_id | company\_id | country |
| ------------- | ---------- | ------- |
| my\_user\_17503 | c\_22235455 | US |
| my\_user\_18821 | c\_22235455 | CA |
You can use this source to filter and group results in any experiment that exposed either user\_id or company\_id.
For a dynamic property, it might look like this:
| user\_id | timestamp | company\_id | intent\_segment | spend\_segment |
| ------------- | ---------- | ---------- | -------------- | ------------- |
| my\_user\_17503 | 2023-10-10 | c\_22235455 | high\_intent | high |
| my\_user\_17503 | 2023-10-11 | c\_22235455 | high\_intent | high |
| my\_user\_17503 | 2023-10-12 | c\_22235455 | mid\_intent | high |
| my\_user\_18821 | 2023-10-10 | c\_22235455 | low\_intent | low |
| my\_user\_18821 | 2023-10-11 | c\_22235455 | low\_intent | mid |
| my\_user\_18821 | 2023-10-12 | c\_22235455 | low\_intent | mid |
The first user in this example has their intent\_segment property change on `2023-10-12`. Because Statsig uses the intent\_segment value before the user's exposure, this user may have different intent\_segment values across different experiments.
================================================================================
# Qualifying Events
URL: https://docs.statsig.com/statsig-warehouse-native/configuration/qualifying-events
Updated: 2026-07-11
================================================================================
## Using qualifying events
Statsig uses qualifying events to simulate exposures for power analysis, and to filter exposures to users who triggered a specific event. Setting up a Qualifying Event is identical to setting up an Assignment Source, except they don't require experimental information.
{% callout type="info" %}
The qualifying event must occur after the exposure, as indicated by their respective timestamps.
{% /callout %}
You can use context columns to filter the qualifying event for power analysis. For example, you might have a Qualifying Event for page load and filter to different page identifiers for power analyses of experiments on different surfaces.
## Filter by qualifying events
Statsig provides controls for using qualifying events to filter exposures in an experiment in cases of over-exposure.
{% figure %}

{% /figure %}
In the image above:
* Forwarded SDK events from Statsig filter the exposures. This could also be a single-event source or any data source.
* The configuration replaces the actual exposure timestamp with the first observed qualifying event, which makes metric timelines and cohort timelines more accurate.
* The configuration counts only qualifying events from within an hour of an exposure event, which helps avoid issues with late-landing attribution.
* A filter limits events to the `www.statsig.com` domain.
Some customers prefer this flexibility, and many set up qualifying events as individual events to use in their analyses.
You can often identify over-exposed experiments by observing uniformly low participation rate on all metrics.
## Power analysis
Use qualifying events in power analysis to estimate your experimentation population based on how many users historically triggered your proposed trigger event.
{% figure %}

{% /figure %}
You can filter these by event attributes, or by entity properties (e.g. users who triggered this event and are also in a segment or country).
## Example data
| Column Type | Description | Format/Rules |
| ---------------------- | ----------------------------------------------------------------------------------------- | ------------------------------ |
| timestamp | **Required** an identifier of when the qualifying event occurred | Castable to Timestamp/Date |
| unit identifier | **Required** At least one entity to which this metric belongs | Generally a user ID or similar |
| additional identifiers | *Optional* Entity identifiers for reuse across identifier types | |
| context columns | *Optional* Fields which can be used to group by and filter results in exploratory queries | |
For example, you could pull from page load event logging directly and save it as a qualifying event called `Page Load`:
| timestamp | user\_id | company\_id | page\_route |
| ------------------- | ------------- | ---------- | ---------- |
| 2023-10-10 00:01:01 | my\_user\_17503 | c\_22235455 | / |
| 2023-10-10 00:02:15 | my\_user\_18821 | c\_22235455 | /search |
| 2023-10-10 00:03:12 | my\_user\_22251 | c\_9928 | /profile |
================================================================================
# Tags & Teams
URL: https://docs.statsig.com/statsig-warehouse-native/configuration/tags-and-teams
Updated: 2026-07-11
================================================================================
Statsig offers tools to enrich data sources, metrics, and entities with team ownership and tags. These provide a useful way to organize your data configuration, and also feed into advanced analytics tools like [meta-analysis](/statsig-warehouse-native/features/meta-analysis).
## Tags
Experiments, Metrics, and Sources can all be tagged for easy discovery, search, and context. You can bulk-add metrics that share a tag to scorecard or guardrail metric sets.
Statsig also provides a Core tag, which identifies company key metrics to use as guardrail metrics across experiments.
Go to the [tags page](/metrics/create-metric-tags) for more information.
## Teams
For larger organizations, the Teams feature adds an organizational and permissions layer on top of a Project. You configure teams at the Project (not Organization) level, and all Project Admins can edit them by default.
After you configure teams and assign a user to a team, any configuration (gates, experiments, metrics, and so on) they create associates with their team and inherits that team's settings. Users who belong to multiple teams can choose which team to associate their configuration with at creation time.
This is useful for managing ownership and review practices. Go to [Teams](/access-management/teams) to learn more.
================================================================================
# Roles
URL: https://docs.statsig.com/statsig-warehouse-native/features/roles-and-access
Updated: 2026-07-11
================================================================================
Statsig has custom, configurable RBAC (role-based access control) across the console. This is especially useful for Warehouse Native customers, because you can control who can update which metrics as well as visibility into raw data.
================================================================================
# Verified Metrics
URL: https://docs.statsig.com/metrics/verified
Updated: 2026-07-11
================================================================================
{% callout type="info" %}
This is an Enterprise feature. Reach out to the support team, your sales contact, or the
[Slack community](https://statsig.com/slack)
to enable it.
{% /callout %}
Verified metrics identify metrics that are curated by your company and known to be trustworthy. When users set up experiments and search for metrics, a verified icon appears next to verified metrics.
{% figure %}

{% /figure %}
To verify a metric, use the overflow menu on the metric.
{% figure %}

{% /figure %}
Admins can control which roles can verify metrics.
{% figure %}

{% /figure %}
================================================================================
# Macros
URL: https://docs.statsig.com/statsig-warehouse-native/configuration/query-tools
Updated: 2026-07-11
================================================================================
In **Metric** and **Assignment sources**, you can use Statsig Macros to directly inject a DATE() type that is relative to the experiment period being loaded.
* `{statsig_start_date}`
* `{statsig_end_date}`
For example, in an incremental reload from `2023-09-01` to `2023-09-03`, this query:
```sql
SELECT
user_id,
event,
ts,
dt
FROM log_table
WHERE dt BETWEEN `{statsig_start_date}` AND `{statsig_end_date}`
```
resolves to
```sql
SELECT
user_id,
event,
ts,
dt
FROM log_table
WHERE dt BETWEEN DATE('2023-09-01') AND DATE('2023-09-03')
```
This is useful because you can inject filters into queries with joins or CTEs and confirm that the initial scan is pruned.
Statsig adjusts the range as necessary in some cases. For example, in entity properties, Statsig adds a pre-experiment buffer to allow for late-landing property data, then selects the most recent value as of each unit's exposure. For CUPED, Statsig adjusts the range to include the pre-experiment window for CUPED calculations.
## Timezone note
All timestamps in Warehouse Native from Statsig are in UTC, and Statsig assumes timestamps in the warehouse are timezone-less/UTC as well.
## Advanced macros
* `{statsig_start_date_int}`
* `{statsig_end_date_int}`
Int versions of the above for use with number based partitioning - e.g. '2025-03-01' => 20250301
* `{statsig_experiment_start_timestamp}`
Only available for metric sources and entity property sources; this resolves to the start timestamp of the experiment. In non-experiment contexts, it resolves to `TIMESTAMP('1970-01-01')`. This is useful for generating entity properties such as "30d revenue before the experiment started".
================================================================================
# Metric Family
URL: https://docs.statsig.com/metrics/metric-family
Updated: 2026-07-11
================================================================================
Managing a large catalog of metrics that are slight variants of one another can be difficult. A common challenge is ensuring that each metric variant inherits the same changes when the metrics need to be updated.
Metric Families let you create metric variants as child metrics of a parent metric. When you update a parent metric, the changes automatically cascade to its child metrics, keeping all related metric definitions consistent.
{% callout type="info" %}
Child metrics of **SUM** and **COUNT** parent metrics support these modifiable configs:
- Add metric source filter
- Outlier Management
- Variance Reduction
- Cohorts and Delayed Data
{% /callout %}
## Create a child metric
To create a child metric, select the **Create Child Metric** option in the dropdown menu in the top right corner of a metric.
{% figure %}

{% /figure %}
You can track the child metrics for a given parent metric using the metric family icon in the top right.
{% figure %}

{% /figure %}
## Deleting and archiving
When a parent metric is deleted or archived, all of its child metrics are deleted or archived as well. Re-enabling the parent automatically restores those child metrics. You can't restore child metrics disabled through the parent individually; you can only re-enable them by restoring the parent metric.
{% figure %}

{% /figure %}
If a child metric is deleted or archived independently of its parent, it can be restored on its own, but it returns as a standalone metric without its original parent-child relationship.
## In experiments
Selecting a Parent Metric in Experiment setup adds all of its child metrics as well. You can remove any metrics you don’t need in the experiment.
{% figure %}

{% /figure %}
================================================================================
# WHN Console API
URL: https://docs.statsig.com/statsig-warehouse-native/configuration/console-api
Updated: 2026-07-11
================================================================================
Statsig's console API allows you to programmatically perform CRUD operations on all configuration objects. Use the API for bulk edits, or to sync your definitions into Statsig as a triggered or scheduled job.
You can also use this API to trigger pulse results and manage workflows in Statsig.
[Go to the full Console API Docs.](/console-api/introduction)
================================================================================
# Semantic Layer Sync
URL: https://docs.statsig.com/statsig-warehouse-native/configuration/semantic-layer-sync
Updated: 2026-07-11
================================================================================
If you have centrally defined metrics, Statsig can sync its data sources and metrics as part of your data version management workflow. Using Statsig's [Console API](/console-api/introduction), you can automatically sync changes to matching definitions in Statsig, and optionally make the metrics read-only in the Statsig console.
A demonstration [GitHub repository](https://github.com/statsig-io/semantic_layer) uses [a script](https://github.com/statsig-io/semantic_layer/blob/main/.github/scripts/statsig_sync.py) executed by [a GitHub Action](https://github.com/statsig-io/semantic_layer/blob/main/.github/workflows/statsig_sync.yml). This setup automatically synchronizes changes to .yml files in the /metrics or /metric\_sources directories in the repo. After you create or update these files, the script updates existing metrics or metric sources in Statsig, or creates new ones accordingly.
To use this example template, follow these steps:
1. Fork [this repository](https://github.com/statsig-io/semantic_layer) to get started.
2. In your forked repository, add your Statsig Console API Key to GitHub Secrets.
3. Update the metric definitions to match your data needs.
4. Validate the automation by modifying relevant files and observing the triggered GitHub Action.
## Detailed guide
### Forking the repository
1. **Fork this repository** to create a copy in your GitHub account.
{% figure %}
{% /figure %}
### Adding the Statsig Console API Key
2. Navigate to `Settings > Secrets and variables > Actions` in your repository settings. Create a new secret named `STATSIG_API_KEY` with your Statsig Console API key as its value. This key authenticates the synchronization process with the Statsig Console API.
{% figure %}
{% /figure %}
### Customizing metric definitions
3. Metric definitions are in the `./metrics` directory, and metric source definitions are in the `./metric_sources/` directory. To customize:
* Use the Statsig Console API to fetch an existing **metric\_source** or **metric** using GET requests for [metric sources](/console-api/introduction#post-/metrics/metric_source/-name-) and [metrics](/console-api/introduction#get-/metrics/-metric_id-).
* Remove the provided example metrics and metric sources, and replace them with your definitions in `./metric_sources/*.yml` and `./metrics/*.yml`.
For readability, the examples use `metric.metricDefinition[]` instead of `metric.warehouseNative[]`. You can view this change [in the sync script](https://github.com/statsig-io/semantic_layer/blob/1611a68703caf18d7fa32088ff06d568d8b3b03a/.github/scripts/statsig_sync.py#L38). Adjust the mapping or revert to `metric.warehouseNative[]` in your definitions as needed.
### Verifying automation
4. To test, edit a metric or metric source description in your repository. This triggers the GitHub Action, visible under the `Actions` tab. The process then creates or updates your metrics and metric sources in Statsig based on the repository's semantic definitions.
{% figure %}
{% /figure %}
{% figure %}
{% /figure %}
{% figure %}
{% /figure %}
This example is a basic template. Test and extend it to meet production standards. Share feedback or improvements with the Statsig support team, your sales contact, or in the [Slack community](https://statsig.com/slack).
================================================================================
# Understanding Experiments
URL: https://docs.statsig.com/statsig-warehouse-native/features/understanding-experiments
Updated: 2026-07-11
================================================================================
Running statistical analysis for an experiment can be complex. Statsig abstracts much of this complexity, which commonly leads to questions like the following:
* What do different date ranges mean?
* What metric data is included in the calculation?
This page gives an overview of the basic settings in Statsig and what they mean for the analysis that runs.
For more advanced settings, refer to [experiment configuration](./experiment-options)
## Date ranges
The date range of an experiment controls the filters used when querying both exposure and metric data. For example, an experiment with a start date of `2025-02-01` and an end date of `2025-02-14` queries assignment data between those two dates, and also queries metric data between those two dates.
There are a few exceptions:
* Metric Bake Windows or Cohort Windows with `wait for...` enabled: cohorts that haven't been in the experiment for the duration of their cohort/bake window are excluded from both the exposure count and metric data for the metric in question.
* If `allow cohort metrics to bake after experiment end` is enabled: the end date of analysis is extended artificially (only for cohort metrics) to maximize data collection for experiments with a one-time intervention where the experiment effect is expected to persist after the treatment is turned off.
### Exposure timing
Statsig includes a given experimental unit’s (for example, a user’s) data only after that unit triggers the experiment. For example, if you are testing a new notification, metric data from before a given user sees the notification shouldn't be included in the experimental analysis.
There is an exception: if a metric source uses a date column as its timestamp, Statsig doesn't include metric data from the day of exposure in the calculation. This happens because a timestamp cast from a date defaults to the first second of that date (00:00:00), which falls before the exposure time.
To address this, the metric source has a setting called `Treat timestamp as date`:
* If checked: Statsig includes day-0 data by converting the join condition to use the cast date (ignoring time).
* If unchecked: Statsig excludes day-0 data from the join between exposure and metric for the reason described above.
Statsig recommends including day-0 data (checking `measure timestamp as day`) if data has been preprocessed at a daily grain.
### Explore query dates
Explore queries can filter metric data or assignment data independently. The default filtering option is metric data. This allows you to exclude certain dates with buggy or non-representative data, or to scope an analysis to recent periods (for example, if your hypothesis is that the treatment effect will take a long time to develop).
Under the advanced tab, it is also possible to:
* filter to exposures in a date range (or outside of)
* filter to exposures within a certain cohort (e.g. X-Y days since exposure)
### Selecting dates
Regardless of [turbo mode](/statsig-warehouse-native/features/turbo) when using incremental reloads, the date picker next to pulse can be used to view pulse "as of" a certain date. This is useful for reviewing historical discussion of an experiment or understanding how results have evolved over time. This data can also be viewed through the time series views in Statsig.
{% callout type="note" %}
Do not use this feature to cherry-pick dates with favorable results.
{% /callout %}
## How experiment calculations work
For every experiment analysis, the basic flow is:
* Identifying when units first saw or were enrolled into the experiment.
* Identifying what those units did (metric data: events or other rollups) after seeing the experimental variant they were assigned to, up until the end of the experiment. This results in a tagged dataset where metric data is associated with a group.
* Aggregating that metric data over the experiment duration. Go to the [metrics documentation](/statsig-warehouse-native/configuration/metrics) for descriptions and SQL snippets describing this step.
* Calculating group-level statistics to be used in the final scorecard analysis. At a high level, the analysis requires the observed totals/means per group, and the variance, which is used to understand how meaningful that difference is (typically statistical significance in frequentist analysis, or probability of best in Bayesian analysis).
* Means are normalized per unit. For basic aggregations this imputes 0s: the calculation is the total value over the count of units exposed. For ratios and means, Statsig computes the means as the sum of the numerator over the sum of the denominator.
================================================================================
# Configuring Experiments
URL: https://docs.statsig.com/statsig-warehouse-native/features/experiment-options
Updated: 2026-07-11
================================================================================
## Configuration
Statsig Warehouse Native offers extensive configurability in experiment setup and analysis. You can run an experiment with default options and get powerful, trustworthy statistical analysis of your results, but in many cases an advanced configuration helps maximize statistical power and measure exactly what you intend to measure.
This page is a glossary for advanced experimentation settings: what they do, how to use them, and tradeoffs to consider.
## Experiment settings
## Basic settings
### Hypothesis
Hypotheses are required to run experiments in Statsig. Hypotheses should specify what an experiment aims to accomplish and how that is measured.
### Primary and secondary metrics
These are the metrics used as the evaluation criteria for your experiment. Generally, Statsig recommends a small number of primary metrics as your overall evaluation criteria, and putting guardrails and exploratory metrics into Secondary.
Statsig provides project-level configuration of the maximum number of primary/secondary metrics; this behavior varies by company and industry, as well as the complexity of the space being measured.
### Experiment duration
In experiment setup there are fields for *Experiment Measured In* and *Target*. These configure how long your experiment runs, influencing sequential testing as well as notifications/timeline alerts.
{% callout type="note" %}
Statsig's [power analysis tools](/statsig-warehouse-native/features/power-analysis) are the best way to determine what the target duration should be. You can attach a power analysis to an experiment to add context on the duration.
{% /callout %}
### Experiment configuration
### Assignment source and groups
For analysis-only experiments, Statsig pre-fills this section from the observed data in the data warehouse. You can configure images and descriptions. Statsig infers group sizes, but you should review and correct them if they don't match the intended traffic split.
To update this, reset the experiment from the decision menu after it has started.
### Groups and parameters
For end-to-end experiments (experiments using Statsig for both assignment and analysis), this section is where you configure targeting, layers, and the groups and associated parameters. Statsig automatically associates exposures generated from the setup with this experiment for analysis.
### Advanced settings
A large number of advanced settings are available for customizing analysis. These can have complex interactions with data.
### Pre-computed user dimensions
Configure dimensions as default breakdowns in pulse. For example, specify a user dimension such as country here to make it available in the scorecard results with daily loads.
This lets you skip scheduled explore queries for this dimension, and the results appear inline in the scorecard.
### Stratified sampling
Stratified Sampling lets you balance experiments across behavior or segments. Statsig tests random salts and picks the one that best balances user attributions. You can partially achieve this during analysis using [CURE](/statsig-warehouse-native/cure/introduction).
Refer to [Stratified Sampling](/experiments/advanced-setup/stratified-sampling) for more details.
### ID type and secondary ID type
You configure the ID type, which is the unit of randomization for an experiment, when setting up the experiment. This is a critical field that represents the kind of entity being experimented on. For example, if splitting traffic randomly per user, this should be `User ID`. If splitting traffic by company, this should be `Company ID`.
Secondary ID types associate metrics from a different ID with the unit of randomization. For example, when running an experiment on a logged-out cookie ID, specifying `UserID` as a secondary ID allows analysis of `UserID` metrics like revenue while keeping `Cookie ID`s as the unit of analysis (for example, the denominator in means). This lets you understand the downstream impact of experiments without introducing survivorship bias or other bias to the analysis.
This mapping can come directly from the assignment source. If multiple exposures are logged for a given unit, and at least one has both ID types, Statsig identifies that these two IDs are mapped to each other. Alternatively, you can specify mappings in an Entity Property Source: provide a mapping table of `ID1` to `ID2` and Statsig connects the data during analysis.
You can configure secondary ID mapping as an enforced 1:1 mapping or as first-touch attribution. Refer to the [ID Resolution documentation](/statsig-warehouse-native/features/id-resolution) for more details.
### Allocation and cohorting
**Configure Allocation Duration**
If using a persistent assignment SDK ([docs](/client/concepts/persistent_assignment)), this setting controls when to stop enrolling users into the experiment.
This setting can also be used without a persistent assignment SDK to filter out users exposed after the duration period. This is useful for enforcing even cohorts across a user base. For example, if a metric takes 7 days to mature and stopping the experiment halts its effect, set the allocation duration to 14 days and run the experiment for 21 days. This analyzes the first 14 days of exposed units while capturing all 7 days of the last cohort's metric behavior.
**Configure Analysis Period**
This setting controls the dates from which Statsig collects metric data. It is useful for truncating the analysis window while continuing to run an experiment.
**Allow cohort metrics to mature after experiment end**
This setting allows cohort or baked metrics that take time to mature to continue collecting data after the end of an experiment. This is only recommended if an experiment is a one-time intervention.
For example, consider a 14-day experiment on new users that modifies a signup page. Removing the changes to the signup page doesn't impact users who already saw it. To maximize data for the "first-week revenue" metric, enable this setting so data continues to collect for users enrolled on day 14 until day 21, and for users enrolled on day 10 until day 17, even after the experiment ends on day 14.
### CURE covariates
This section lets you configure covariates for CURE. Configure strong defaults in the project settings, and use this section to add relevant/domain-specific covariates. Refer to the [CURE](/statsig-warehouse-native/cure/introduction) documentation for more details.
### Analysis settings
**Analytics type**
Whether to use Frequentist or Bayesian analysis. You can't change this once an experiment starts, to avoid cherry-picking methodology.
**Apply Sequential Testing \[Frequentist Only]**
Controls whether [sequential testing](/experiments/advanced-setup/sequential-testing) is applied. Statsig recommends this setting to avoid false positives from peeking.
**Bonferroni/Benjamini-Hochberg \[Frequentist Only]**
Configures multiple-comparisons corrections, either controlling the false positive rate or false discovery rate. Refer to the more detailed documentation for [Bonferroni](/statsig-warehouse-native/features/statistics/methodologies/bonferroni-correction) and [Benjamini-Hochberg](/statsig-warehouse-native/features/statistics/methodologies/benjamini-hochberg-procedure).
**Default Confidence Interval/Chance to Beat Threshold**
The confidence level used for this experiment. The default is 95%, which is the recommended industry standard. Depending on the risk profile of the experiment, a stricter or less strict setting may be appropriate.
**Use Informative Priors \[Bayesian Only]**
For bayesian experiments, whether to use informed priors in analysis, and the configuration for them.
**Turbo Mode**
Whether to use [Turbo Mode](/statsig-warehouse-native/features/turbo) to run experiment reloads more quickly.
**Filter Exposures by Qualifying Event**
This setting allows filtering exposures to experimental units that did (or did not) trigger a secondary event beyond exposure. This is useful for analysis-only experiments on web experimentation platforms that over-expose heavily. For end-to-end experiments, use Statsig to expose only at the point of intervention.
This setting has a few inputs:
* A qualifying event, which Statsig joins to exposures to determine if users triggered an event
* *Exclude Matching Units*: if toggled, Statsig drops units that triggered this event from the analysis. If unchecked, Statsig filters the analysis to units that triggered the event.
* *Use qualifying event timestamp for first exposures*: if checked, Statsig replaces units' exposure timestamp with the qualifying event's timestamp. This is useful for small cohort windows, for example, measuring whether something happened within 10 minutes of the intervention. If the intervention occurred several minutes after the original exposure event, using the actual time the user saw the intervention can be helpful.
* *Filter events by time window*: only consider qualifying events for the inclusion/exclusion filters that occurred within a certain time from the exposure. This is useful if a user may return and re-trigger the qualifying event when it is no longer relevant to the exposure.
**Filter Assignment Source**
This setting controls additional filters applied to the exposure data for this experiment. This can be useful to filter out bad dates with data known to be biased or non-representative, or to filter to a specific subset of interest for the scorecard results.
**Default Date Filter**
Statsig calculates results across multiple rollups, including Cumulative, 7 days, and 1 day. Cumulative is almost always the correct choice for this setting because it maximizes statistical power. You can select other views in the scorecard.
## Explore (custom) query settings
Explore queries enable drilldown, filtering, grouping, and more advanced ways to cut metric data by user and metric properties. They provide a way to analyze experiment results in depth. Explore queries run with the same statistics as the scorecard; with no filters or other settings applied, results match the scorecard results.
## Metrics
Pick the metrics to run the explore or drilldown analysis on. This can include tags and local metrics.
## Group By
This setting is the primary tool for explore and custom queries. It allows drilldowns into user segments to understand differentiated performance. You can combine it with [differential impact detection](/experiments/exploring-results/differential-impact-detection) to analyze experiment results beyond topline averages.
By default, Statsig shows only the top 10 dimension levels with over 100 units per segment in results. The rest are grouped into an OTHER category. This prevents inflated false positive rates and keeps statistical analysis rigorous. Contact the Statsig team in Slack if you need this limit adjusted.
## Filter
Use filters to include or exclude certain cohorts of units in an explore analysis by specifying the property and a filter set.
## Time range for metric data
This setting filters metric data to a specific date period, for example the three days from 2024-04-10 to 2024-04-12. This is useful for investigating data anomalies, analyzing a recent cohort when results have changed significantly, or running drill-down analyses.
## Filter by exposure date
In some cases it is useful to filter to units that entered the experiment on a specific date, or to exclude them (for example, units exposed on a holiday may behave atypically). You can specify filters based on user exposure date.
By switching the mode to Days Since First Exposure, you can also drill down into each cohort's behavior during a specific window after exposure. This is a useful way to understand behaviors observed in the days-since-first-exposure timeline view.
## Scheduling
After running a query, you can schedule it. Scheduled queries run daily after any scheduled pulse load to update results. Scheduled queries can also be added to the experiment Summary page as part of the experiment writeup and downloadable summary.
## Interaction effect detection
Explore queries can trigger [interaction effect detection](/experiments/exploring-results/interaction-detection) analyses to determine whether two experiments are interacting in a synergistic or harmful way.
================================================================================
# Configuring Experiments
URL: https://docs.statsig.com/statsig-warehouse-native/features/configure-an-experiment
Updated: 2026-07-11
================================================================================
Setting up an experiment is a core flow in Statsig. In Warehouse Native, there are two modes. In one, you connect to existing assignment data from your warehouse. In the other, you configure the experiment in Statsig, use the Statsig SDK, and then analyze the resulting exposures in your warehouse.
This page covers creating and configuring an experiment for analysis in Statsig. For most A/B/n experiments, the default settings produce a standard, powerful analysis.
## Creating an experiment
To create an experiment, go to the experiments tab in your console and click the create button.
There are two types of experiments in Statsig Warehouse Native:
* Analyze: these are for 3rd-party or in-house exposure sources
* Assign & Analyze: these are Statsig-configured experiments. You can set up all of the configuration here, implement it through Statsig SDKs, and track results.
{% figure %}

{% /figure %}
Next, give the experiment a name, specify your hypothesis, and select the experiment from your exposure sources. Sync sources if your exposure isn't available in the dropdown.
Specify the control group and the ID type the experiment uses.
{% figure %}

{% /figure %}
## Choose metrics
On the experiment setup page, refine your hypothesis and add the Primary and Secondary metrics for your experiment.
* Primary metrics: a short list (1-3) of metrics that define your overall evaluation criteria. Typically one target metric and one first-mile metric, such as revenue and checkout clicks.
* Secondary metrics: guardrail and explanatory metrics. These are observational in nature and are less conclusive. Applying Bonferroni corrections is one way to formalize this approach, though it can be overly conservative.
{% figure %}

{% /figure %}
## Choose groups
Statsig infers the groups and group splits in your experiment. Check and correct the splits to ensure SRM checks work as intended.
You can also rename groups or delete irrelevant groups.
{% figure %}

{% /figure %}
## Advanced settings
Statsig has many configurable settings for experiments. You can set defaults at the org level. Available settings include:
* Frequentist vs. Bayesian analysis
* Target duration of the experiment
* Whether to apply [Sequential Testing](/experiments/advanced-setup/sequential-testing) adjustments
* Allocation Duration
* Stop enrolling new users into the experiment after X days
{% callout type="note" %}
You need to set up [Persistent Assignment](/client/concepts/persistent_assignment) to retain already-enrolled users to stay in the same group
{% /callout %}
* Cohort Duration
* Specify a timeline for collecting metric data
* Include only units with a completed duration, so Statsig doesn't include units with incomplete data in Pulse
* Whether to allow cohort metrics to mature after experiment end
{% callout type="note" %}
After you configure this value, it overwrites the `only include units with a completed duration` setting above.
{% /callout %}
* [ID stitching](/statsig-warehouse-native/features/id-resolution)
* Whether and how to apply [Bonferroni Correction](/experiments/statistical-methods/methodologies/bonferroni-correction)
* Default Confidence Intervals
* Default rollup windows for result readouts (cumulative, 1, 7, 14, or 28 days from the analysis date)
* Turbo Mode
* Skip time series calculations to reduce cost and runtime when you only need the overall effect
* Filter exposures by qualifying event
* Only applicable in Analyze-only experiments
* Filter assignment source
* Add optional filters on the experiment's assignment source. By default, the assignment source is filtered to the experiment and groups being analyzed. Use this setting to exclude known bad actors from the primary scorecard or to analyze a sub-population in the main scorecard.
## Testing in a lower environment
After experiments are launched, you can't edit the groups without restarting the experiment, because Statsig has already allocated users to each group. Test each experiment in lower environments before starting. Click the **Test** button on the experiment setup page, then select **Enable for Environments**. These environments should match your [SDK environment setup](/guides/using-environments/#configuring-environments). Testing in a lower environment and using [overrides](/experiments/setup/overrides) lets you manually set your experiment group to test each variant.
{% figure %}

{% /figure %}
When an experiment is enabled for lower environments, Warehouse Native customers can check exposure logs to troubleshoot exposures. Experiment results and cumulative exposures aren't available in lower environments.
## Start the experiment
After completing setup, click **Save and Analyze** to start the experiment. You are prompted to finalize the dates, experiment status, and optionally configure a schedule to reload experiment results.
{% figure %}

{% /figure %}
When you click **Load Data**, a Pulse analysis starts and you are taken to the results page.
## Data freshness
For a Statsig-configured experiment using the Statsig SDKs to generate exposures, the default is that exposures are batched, deduplicated, and written to your warehouse once a day. When you launch an experiment, early metric data can help detect crashes or significant bugs. When Pulse is loaded shortly after the experiment starts, Statsig updates exposures in your warehouse before computing Pulse results. This allows you to see Pulse results as fresh as approximately 15 minutes, assuming events and metrics arrive at the same speed.
If the number of exposures on an experiment is fewer than 1 million, Statsig performs a just-in-time update of exposures in your warehouse when Pulse is loaded.
{% callout type="note" %}
Statsig only writes exposure information to your warehouse that it uses for experiment analysis. Statsig doesn't write exposures from pre-production environments or overrides, because it doesn't use them in analysis.
{% /callout %}
================================================================================
# Types of Experiments
URL: https://docs.statsig.com/statsig-warehouse-native/features/types-of-experiments
Updated: 2026-07-11
================================================================================
## Analysis only
### A/B/n
Analysis-only A/B/n tests run analysis on top of assignment and metric data from your warehouse. In these experiments, Statsig operates as a statistics engine to help make analyses more reproducible.
## Assign and analyze
### A/B/n
The most common Assign-and-Analyze experiments are A/B/n tests that integrate warehouse data with Statsig's live SDK. In these experiments, Statsig operates as an assignment tool, a real-time diagnostics tool, and as a statistics engine. Assignment related features supported include:
> #### [Stratified Sampling](/experiments/advanced-setup/stratified-sampling)
>
> Dividing your population into homogeneous groups (based on a metric or classification).
> #### Configurable Allocation Duration
>
> You can enroll users for a subset of an experiment's duration. If you are experimenting on a one-time experience (such as signup flows), no additional configuration is needed. If the experience isn't one-time (enrolled users need to keep being assigned), configure your SDKs to use Persistent Assignment (you provide a store to save user enrollment states; the SDK manages this state). Go to Persistent Assignment for [Client](/client/concepts/persistent_assignment) and [Server](/server/concepts/persistent_assignment) SDKs.
### Switchback experiments
Statsig offers [Switchback](/experiments/types/switchback-tests) tests for experimenting in the presence of meaningful network effects or in ecosystems where changing an experience for one group affects other groups (for example, a ride-service app changing prices for some users changes driver demand for all users).
You can configure Switchback tests in Statsig's console. They use time periods (and optional buckets such as city or country) to alternate experiment conditions and run a bootstrapping analysis to estimate test statistics. Advanced options include burn-in and burn-out periods, allocation windows, and configurable window lengths.
### Geo testing
Statsig offers [Geotesting](/statsig-warehouse-native/geotests/introduction) to support marketing and product causal inference techniques where you can't run a traditional A/B test. Geotesting treats geographic units (such as postal codes and DMAs) as the unit of analysis, enabling new experimental methodologies like rigorous testing of paid marketing or search on platforms like Google and Facebook Ads.
Geotesting uses Synthetic Control methodologies built on GeoLift, an open-source package from Meta. All existing metrics and metric sources are available, with the addition of geographic labels.
### MABs
* [Autotune](/statsig-warehouse-native/features/autotune) is Statsig's multi-armed bandit solution. It balances explore and exploit to deliver the optimal global treatment to users, making it useful for evaluating many options and dynamically adjusting traffic to avoid over-delivering underperforming variants.
* Statsig also offers a [Contextual Multi-Armed Bandit](/autotune/contextual/introduction), which extends the multi-armed bandit by personalizing the experience served to users based on "context" (user or event attributes provided to the Statsig SDK). This balances explore and exploit by optimizing for potential upside in its predictions.
Both MAB types are invoked through Statsig's `getExperiment` call. For the CMAB approach, provide relevant attributes to the user object. Contact the Statsig support team, your sales contact, or the [Slack community](https://statsig.com/slack) for assistance with Contextual Multi-Armed Bandits.
================================================================================
# Power Analysis
URL: https://docs.statsig.com/statsig-warehouse-native/features/power-analysis
Updated: 2026-07-11
================================================================================
Establishing a run time for an experiment is good practice for two reasons:
* It aligns teams around timelines.
* For fixed-horizon analysis, it establishes when it is valid to draw a conclusion from the results.
To determine run time, establish the minimum size of impact you want to reliably detect (known as the MDE, or minimum detectable effect), then run an analysis to determine how many samples and how much time you need to achieve that MDE.
### Running a power analysis
To run a power analysis in Statsig, provide two inputs:
* A population
* This step is important because most experiments reach only a subset of users, and those users may behave differently from the overall population.
* You can base the population on an experiment you already ran, or on a Qualifying Event.
* A qualifying event is an arbitrary set of historical user-timestamp pairs. For example, if you plan to expose users on a button click, provide the users who clicked that button in the preceding week.
* Metrics
* Enter the metrics you plan to use as your evaluation criteria. You can add multiple metrics, which is useful for analyzing which metrics will be more or less sensitive in your target population.
{% figure %}

{% /figure %}
### Readout
Statsig simulates an experiment based on your input, calculating population sizes and relative variance from historical behavior.
The power analysis readout shows a week-by-week view of the experiment statistics you can expect.
In the settings section, you can specify:
* **Number of Experiment Groups**: The total number of groups in the experiment, including control.
* **Control Group %**: The percentage of users in the control group, for example 50% if half of all users are control.
* **Fixed Allocation or Fixed MDE Analysis**: The type of analysis to run. Go to [Analysis Types](/experiments/power-analysis/#analysis-types) for details.
* **One-sided or Two-sided test**: The type of z-test to use for the analysis.
* **Significance Level (α)**
* **Power (1-β)**
* **Bonferroni Correction Per Variant**: Whether to include an α correction for multiple tests in the power analysis.
Updating these settings recalculates results based on the analysis that already ran.
================================================================================
# Stratified Sampling
URL: https://docs.statsig.com/statsig-warehouse-native/features/stratified-sampling
Updated: 2026-07-11
================================================================================
## What is stratified sampling
Stratified sampling divides the entire population into homogeneous groups called strata (singular: stratum), then selects random samples from each stratum. For example, if you have XS and XL customers and randomize them into Control and Test groups, both groups should be balanced across XS and XL customers. You can also stratify based on a metric like Revenue/User.
With large populations, randomization typically achieves this balance. In B2B scenarios and other low-volume or high-variance situations, stratified sampling ensures the balance explicitly. Statsig supports both automated and manual stratified sampling. When a small number of power users drive a large portion of an overall metric value, stratified sampling meaningfully reduces false positive rates and produces more consistent, reliable results. Statsig simulations showed approximately a 50% decrease in the variance of reported results.
## Automated stratified sampling
### How it works
The Statsig SDKs use a *salt* to randomize or bucket experiment subjects ([learn more](/faq#how-does-bucketing-within-the-statsig-sdks-work)). When you enable stratified sampling, Statsig tries 100 different salts and evaluates how balanced the resulting groups are. Balance is evaluated using either a metric or an attribute you provide that describes your experiment subjects. The best salt from this set is selected and saved. [Learn more](https://statsig.com/blog/introducing-stratified-sampling).
{% figure %}

{% /figure %}
The selection space for salts is large enough that stratifying multiple experiments on the same metric doesn't result in overlap. In Statsig simulations, the groups were as independent as the literature predicts.
### Enabling stratified sampling
You can enable stratified sampling under Advanced Settings on the experiment setup page. There are two ways to stratify on Statsig. If you choose a metric to stratify on, Statsig uses that metric to balance the groups.
{% figure %}

{% /figure %}
If you instead choose an attribute or classification (for example, S, M, L, XL), Statsig uses that to balance the groups.
* On Statsig Cloud, you'll upload a CSV (in Early Access)
* On Statsig Warehouse Native, you'll use Entity Properties
{% figure %}

{% /figure %}
After you select the Stratify button, Statsig analyzes a set of salts and picks the best one.
{% figure %}

{% /figure %}
## FAQ and best practices
* **What population is used when balancing?**
* When evaluating salts, Statsig computes balance using pre-experiment data for the entire targeted population of the experiment’s unit type (e.g., all `userID`s or all `customerID`s) over the selected lookback window. There is no filtering on exposure because the experiment has not started yet.
* **How are new units handled after stratification?**
* Units that weren't present in the pre-experiment data are still assigned deterministically by the chosen salt, i.e., effectively at random with respect to the balancing metric. They don't influence the salt selection and may introduce some drift from the initial balance.
* **Should I use stratified sampling for every experiment?**
* Not necessarily. It’s most useful when you expect imbalance due to heterogeneous units (e.g., “whales”) or skewed metrics. The tradeoff is time/compute cost that scales with the number of units and adds steps before starting an experiment. If you don’t expect meaningful imbalance, a standard random split is generally recommended.
* **Does salt evaluation assume 100% allocation? What about running at less than 100%?**
* Yes. All candidate salts are evaluated assuming 100% of the targeted population is allocated. If you then run the experiment at an allocation below 100%, random sampling of that subset can reintroduce imbalance (e.g., by chance, some high-impact units may fall disproportionately into one arm). For the period you care most about inference, prefer 100% allocation to preserve the intended balance. Lower allocations are best used briefly for safe rollouts rather than for the full experiment duration.
* **Across candidate salts, is it the same set of users being evaluated?**
* Yes. Candidate salts are assessed over the same targeted population; only the randomization induced by the salt changes.
* **How long does stratification take?**
* Duration depends on the number of units and the metric/source being queried. There is no fixed SLA; larger populations take longer.
## Manual assignment for stratified sampling
When setting up an experiment, you can configure overrides (for example, force user X or Segment A into Control, force user Y or Segment B into Test). Overrides are intended for testing; overridden users are excluded from experimental analysis in Pulse results. To include manually assigned users in stratified sampling analysis, select the *Include Overrides in Pulse* checkbox. This includes overridden users in all metric lift analyses. You can assign 100% of experiment participants manually, or assign a subset manually and randomly assign the rest.
{% callout type="note" %}
You can add overrides for an ID type that differs from the experiment's ID type, but Statsig won't resolve those ID evaluations to the experiment's ID type and they won't contribute to Pulse results.
{% /callout %}
When you use the Statsig SDK for assignment, the SDK handles randomization. When you control assignment of users, you are responsible for ensuring users are balanced across experiment groups.
{% figure %}

{% /figure %}
## Additional reading
[Morgan and Rubin 2012](https://projecteuclid.org/journals/annals-of-statistics/volume-40/issue-2/Rerandomization-to-improve-covariate-balance-in-experiments/10.1214/12-AOS1008.full) covers the history, philosophy, and proofs of re-randomization, including how re-randomization reduces the randomization variance of the difference in means. The paper notes that "standard asymptotic-based analysis procedures that do not take the re-randomization into account will be statistically conservative." Statsig remains conservative with the t-test to maintain consistent and comparable results across methods.
[Lin & Ding 2019](https://arxiv.org/abs/1906.11291) is additional reference reading on this topic.
================================================================================
# Differential Impact Detection
URL: https://docs.statsig.com/statsig-warehouse-native/features/differential-impact
Updated: 2026-07-11
================================================================================
{% partial file="whn/differentialImpact.mdx" /%}
================================================================================
# Targeting
URL: https://docs.statsig.com/statsig-warehouse-native/features/targeting
Updated: 2026-07-11
================================================================================
## How to target experiments
Experiments integrate natively with Statsig's Feature Gates product to target interventions. Feature gates provide a rich language for targeting users by properties or segments. You can give an experiment a targeting gate to restrict an intervention to units that pass that gate. Go to [Feature Gates](/feature-flags/overview) for more information.
## When to target an experiment
Targeting an experiment makes sense when
* You want to gradually release the experiment to tiers of users
* Part of your hypothesis is that the experiment intervention will only work for a target subset of users, e.g. mobile users
================================================================================
# ID Resolution (ID Stitching)
URL: https://docs.statsig.com/statsig-warehouse-native/features/id-resolution
Updated: 2026-07-11
================================================================================
Statsig Warehouse Native supports resolving multiple IDs to one identified user, allowing you to expose an experiment on one identifier and analyze data from one or more mapped identities associated with that experimental unit.
Common scenarios include:
- Exposing logged-out users and analyzing logged-in metrics like revenue, or a funnel from a logged-out marketing page landing to a logged-in subscription purchase.
- Using one-to-many relationships, for example when a single user owns multiple accounts. ID resolution lets you aggregate metrics from the user's mapped accounts. This approach reduces statistical power but is statistically sound.
ID resolution is a common need in experimentation. Without it, the responsibility for identity mapping typically falls on data users or PMs, which leads to inconsistent results and expensive query logic. Advanced ID Resolution centralizes this process, making it consistent and performant, and allowing all users to reference trusted identity tables.
## The Challenge: Connecting User Identifiers
A common challenge in experimentation is linking user identifiers before and after an event boundary, most often signups.
Experimenters typically have a logged-out ID (for example, a cookie or Statsig stableID) and, for users who sign up, a userID created afterward. Because business metrics are typically computed at the userID level, teams often want to randomize on logged-out identifiers but measure outcomes on logged-in metrics like revenue or LTV.
Most platforms require manual joins or preprocessing to connect these identifiers, leading to complex, error-prone queries that must reconcile exposures across time and mapping tables.
Statsig Warehouse Native provides an automatic, no-code way to connect identifiers across these boundaries. The approach is centralized, consistent, and reproducible.
{% figure %}

{% /figure %}
## Mapping Modes
When using ID resolution, you can choose from one of three modes:
- Strict 1:1 mapping enforces that identities have a singular mapping. If you have a mapping between two IDs that are always 1:1, this mode enforces that the mapping is singular and warns you if there is data where that isn't the case. Users with a single identity can use downstream metrics from the secondary identity. Statsig considers multi-mapped users corrupted and discards them from the analysis.
- First-touch mapping attributes activities of secondary IDs to one primary ID based on the first time the user is exposed to the experiment.
- Last-touch mapping attributes activities of secondary IDs to one primary ID based on the most recent time the user is exposed to the experiment.
### Strict 1:1 Mapping
{% figure %}
{% /figure %}
Statsig collects all potential mappings between identifiers within the experiment date range, on the exposed population. If the primary ID has multiple secondary IDs, or vice versa, Statsig considers the record polluted and drops it from the analysis. Choosing this mode changes the exposures on the primary ID because it disqualifies any records outside of a 1:1 mapping.
### First Touch Mapping (Mixed Population)
{% figure %}
{% /figure %}
The direction of first-touch mapping is based on the experiment; all secondary IDs resolve to 1 primary ID, and a single primary ID can have multiple mapped secondary IDs.
Statsig attributes data to the group of the first associated primary ID seen in the exposure. If a secondary ID has multiple associated primary IDs, Statsig uses the group of the first primary ID. Users that cross groups aren't discarded from analysis; instead, Statsig assigns them based on their first experience.
Statsig drops primary ID records that are associated with another primary ID but are not the first observed records from the analysis. If a user is exposed twice on different primary IDs that resolve to the same secondary IDs, Statsig keeps only the primary ID metrics from the first-exposed user in the analysis.
### Last Touch Mapping (Mixed Population)
{% figure %}
{% /figure %}
Same as first touch but Statsig attributes data to the most recent primary ID.
### What mixed population means
{% figure %}
{% /figure %}
Both first-touch and last-touch mapping show pulse results based on a mixed population. Each metric is based on the corresponding population for its unit type. For example, if an experiment randomizes on Stable ID and the scorecard metrics include both Stable ID and User ID metrics, Pulse uses the raw exposure population for Stable ID metrics (to stay true to the randomization process) and the resolved population for User ID metrics, depending on the mapping mode.
### Explanation of Methodology
1. Statsig prefers primary IDs over secondary IDs if present in the data.
The unit type of analysis should match that of randomization. Statsig always prefers the primary ID when it is present in the data. If the primary ID is not present, Statsig uses the most recent secondary ID.
2. Secondary IDs are only used to join metrics to exposures, but the unit of analysis is still the primary ID.
Statsig calculates the unit counts of each metric's results using the primary ID.
3. Statsig handles many (primary) to one (secondary) mapping by attributing the secondary ID to **ONE** primary ID.
4. One (primary) to many (secondary) mapping is implicitly handled by treating all secondary IDs as the same unit.
e.g. The metric value is added in a sum metric or counted in a count metric.
5. Statsig supports a mixture of primary and secondary IDs in the same experiment.
You can use both primary and secondary IDs in the same experiment. For example, when you run a signup experiment, you can measure session-level metrics for the primary ID and user-level metrics for the secondary ID. To do this, Statsig maintains two populations: one for the primary ID and one for the secondary ID. The primary ID population is the same as if you had only used the primary ID.
## How to Enable ID Resolution in a Statsig Experiment
To set up identity resolution in Statsig, either log or join data to provide both IDs on your assignment source, or provide one ID in the assignment source along with a mapping table in the form of an Entity Property Source.
### Using Property Source
To use Identity Resolution across experiments in your project, you need a lookup table that has both the ID you are exposing on and the selected target ID. Configure this table by setting up an Entity Property Source with both IDs present.
After that is done, select this source when configuring your secondary ID type, and Statsig handles the join for you.
{% figure %}

{% /figure %}
If you want to use a Statsig SDK to populate this table, you can log an event (for example, a "Signup" event) that has both the logged-out identifier and the user ID on the same event. Statsig writes events sent through the SDK into your warehouse, and you can configure an Identity Resolution source on top of that:
{% figure %}

{% /figure %}
### Using Assignment Source
When creating an assignment source, provide a column for both ID types. The Primary ID is expected to be non-null for exposure records. The secondary ID can be null. If the secondary ID is sparse (some records are null due to logging), Statsig back-attributes any identified secondary ID to other records from the same primary ID.
{% figure %}

{% /figure %}
When you create an analysis-only experiment or power analysis with this ID type, you can optionally select a Secondary ID. If you do so, you can now use metrics from either ID type in your analysis. For E2E experiments that use the Statsig SDK, this is configurable on the experiment setup page, under Advanced settings.
Internally:
- For metric sources with the primary ID, Statsig joins metrics to exposures based on that primary ID.
- For metric sources with only the secondary ID, Statsig joins metrics to exposures based on that secondary ID.
- In strict mode, Statsig drops users with a duplicate mapping from analysis. In first-touch mode, units use their first exposure record and merge data from all mapped secondary IDs.
This works natively across Metric Sources, so you can set up funnel or ratio metrics across the two ID types.
Analysis uses the primary ID. This process associates metric values from the secondary ID with the corresponding primary ID records.
### Mapping Changes
If you change the entity property source or assignment source's definition or underlying data, Statsig reflects those changes on the next reload. This is **why** a full reload is required, since otherwise historical changes to the mapping can lead to inconsistent data on incremental reloads or explore queries.
### Best Practices
Statsig recommends using an [Entity Property Source](/statsig-warehouse-native/configuration/entity-properties) to provide a cleaned unit mapping from your warehouse. You can also provide mappings on your exposure source by logging multiple identifiers in the exposure data. Statsig uses all available identifiers to match across records.
For both modes, an experiment can only have one mapped ID type, for example secondary_id->user_id or secondary_id->account_id, but not both.
All modes require a full reload to prevent data inconsistency when historical mappings are changed or new mappings are introduced.
Statsig filters the property source or assignment source used to provide mappings to records within the experiment's date range. If a mapping is "evergreen", or not scoped to a specific time period, you can omit the timestamp on the entity property source.
#### Example of a supported schema
if your assignment source data contains: `{stableID: 'unknown_123', exp_id: 'PDP Test', test_group: 'Control'}`
and your metric sources contain data that represents a metric as: `{userID: 'known_abc', event: 'page_load'}`
Your Entity Source or Assignment source must contain the secondary identity (in this case, `userID`) that enables Statsig to join your assignment data with your metric data:
`{stableID: 'unknown_123', userID: 'known_abc', country: 'USA'}`
## Considerations
Deduplicating records can lead to biased results, so Statsig performs two extra health checks on this kind of experiment.
- Statsig checks your deduplication rate and warns you if it is unusually high. Some secondary IDs are expected to have multiple logged-out IDs due to users using different devices or clearing browser history.
- Statsig performs a chi-squared test to evaluate whether the deduplication rate is identical across arms of the experiment. In some cases, an experiment may cause more users to return (for example, an email re-engagement campaign), in which case duplicates are expected to be more frequent in that arm and can be a positive outcome. In this case, you can use first-touch attribution to maintain a common identifier.
Statsig supports breakdowns of metric dimensions for experiment results only on properties associated with primary ID types. Statsig doesn't support secondary ID type dimension breakdown for experiment results and custom queries due to high risk of post-exposure data leaking into the group-by's or filters.
================================================================================
# ID Resolution (ID Stitching)
URL: https://docs.statsig.com/statsig-warehouse-native/features/id-resolution-legacy
Updated: 2026-07-11
================================================================================
At the earliest, Statsig will update its ID resolution methodology to reflect mixed population on November 15th.
## Mapping Modes
When using Advanced ID resolution, you can choose between modes:
* Strict 1:1 mapping enforces that identities have a singular mapping. If you have a mapping between two IDs that are always 1:1, this mode enforces that the mapping is singular and warns you if there is data where that isn't the case. Users with a single identity can use downstream metrics from the secondary identity. Statsig considers multi-mapped users corrupted and discards them from the analysis.
* First-touch mapping applies when units might have multiple mappings in either direction. For example, a single user may have multiple "profiles", or someone may have logged into the same account from several devices or web sessions. In this case, units use the experiment group of their first exposure for analysis and aggregate metrics from all of their associated secondary IDs.
| Strict 1:1 Mapping | First Touch Mapping |
| ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
|  |  |
### Strict 1:1 Mapping
Statsig collects all potential mappings between identifiers within the experiment date range, on the exposed population. If the primary ID has multiple secondary IDs, or vice versa, Statsig considers it polluted and drops it from the analysis.
### First Touch Mapping
The direction of first-touch mapping is based on the experiment; all secondary IDs resolve to 1 primary ID, and a single primary ID can have multiple mapped secondary IDs. If your aim is to only have one secondary ID, you can manage that logic inside the entity property source today, but feel free to reach out to support if there's specific logic you would like to request.
Statsig attributes data to the group of the first associated primary ID seen in the exposure. If a secondary ID has multiple associated primary IDs, Statsig uses the group of the first primary ID. Users that cross groups aren't discarded from analysis; instead, Statsig assigns them based on their first experience.
Statsig drops primary ID records that are associated with another Primary ID but are not the first observed records from the analysis. If a user is exposed twice on different primary IDs that resolve to the same secondary IDs, Statsig keeps only the primary ID metrics from the first-exposed user in the analysis.
### Last Touch Mapping
Same as first touch but Statsig attributes data to the most recent primary ID.
### Note on ID stitching
Multiple secondary IDs attached to one primary ID still count as "one" experimental primary ID; Statsig merges the metric values across records from the different secondary IDs, e.g. added in a sum metric or counted in a count metric.
{% callout type="info" %}
Statsig is interested in supporting more complex 1-to-many relationships of
identities and is eager to partner with customers to develop these
capabilities if a more advanced use-case is required.
{% /callout %}
## How it Works
To set up identity resolution in Statsig, either log or join data to provide both IDs on your assignment source, or provide one ID in the assignment source along with a mapping table in the form of an Entity Property Source.
### Using Property Source
To use Identity Resolution across experiments in your project, you need a lookup table that has both the ID you are exposing on and the selected target ID. Configure this table by setting up an Entity Property Source with both IDs present.
After that is done, select this source when configuring your secondary ID type, and Statsig handles the join for you.
{% figure %}

{% /figure %}
If you want to use a Statsig SDK to populate this table, you can log an event (for example, a "Signup" event) that has both the logged-out identifier and the user ID on the same event. Statsig writes events sent through the SDK into your warehouse, and you can configure an Identity Resolution source on top of that:
{% figure %}

{% /figure %}
### Using Assignment Source
When creating an assignment source, provide a column for both ID types. The Primary ID is expected to be non-null for exposure records. Your secondary ID can be null. If your secondary ID is sparse (some records are null, and some are not due to logging), Statsig back-attributes any identified secondary ID to other records from the same Primary ID.
{% figure %}

{% /figure %}
When you create an analysis-only experiment or power analysis with this ID type, you can optionally select a Secondary ID. If you do so, you can now use metrics from either ID type in your analysis. For E2E experiments that use the Statsig SDK, this is configurable on the experiment setup page, under Advanced settings.
Internally:
* For metric sources with the primary ID, Statsig joins metrics to exposures based on that primary ID.
* For metric sources with only the secondary ID, Statsig joins metrics to exposures based on that secondary ID.
* In strict mode, Statsig drops users with a duplicate mapping from analysis. In first-touch mode, units use their first exposure record and merge data from all mapped secondary IDs.
This works natively across Metric Sources, so you can set up funnel or ratio metrics across the two ID types.
Analysis uses the primary ID. This process associates metric values from the secondary ID with the corresponding primary ID records.
### Mapping Changes
If you change the entity property source or assignment source's definition or underlying data, Statsig reflects those changes on the next reload. This is **why** a full reload is required, since otherwise historical changes to the mapping can lead to inconsistent data on incremental reloads or explore queries.
### Best Practices
Statsig recommends using an [Entity Property Source](/statsig-warehouse-native/configuration/entity-properties) to provide a cleaned unit mapping from your warehouse. You can also provide mappings on your exposure source by logging multiple identifiers in the exposure data. Statsig uses all available identifiers to match across records.
For both modes, an experiment can only have one mapped ID type, for example secondary\_id->user\_id or secondary\_id->account\_id, but not both.
All modes require a full reload to prevent data inconsistency when historical mappings are changed or new mappings are introduced.
Statsig filters the property source or assignment source used to provide mappings to records within the experiment's date range. If a mapping is "evergreen", or not scoped to a specific time period, you can omit the timestamp on the entity property source.
#### Example of a supported schema
if your assignment source data contains: `{stableID: 'unknown_123', exp_id: 'PDP Test', test_group: 'Control'}`
and your metric sources contain data that represents a metric as: `{userID: 'known_abc', event: 'page_load'}`
Your Entity Source or Assignment source must contain the secondary identity (in this case, `userID`) that enables Statsig to join your assignment data with your metric data:
`{stableID: 'unknown_123', userID: 'known_abc', country: 'USA'}`
## Considerations
Deduplicating records can lead to biased results, so Statsig performs two extra health checks on this kind of experiment.
* Statsig checks your deduplication rate and warns you if it is unusually high. Some secondary IDs are expected to have multiple logged-out IDs due to users using different devices or clearing browser history.
* Statsig performs a chi-squared test to evaluate whether the deduplication rate is identical across arms of the experiment. In some cases, an experiment may cause more users to return (for example, an email re-engagement campaign), in which case duplicates are expected to be more frequent in that arm and can be a positive outcome. In this case, you can use first-touch attribution to maintain a common identifier.
================================================================================
# Filter Exposures
URL: https://docs.statsig.com/statsig-warehouse-native/features/filtering-exposures
Updated: 2026-07-11
================================================================================
## Filter Exposures by Qualifying Event
You can use Qualifying Events to filter exposures to units that did or did not trigger an event after being exposed. This is useful for analysis-only experiments where the assignment tool may have over-exposed units (for example, assigning units on page load when the intervention was only triggered when a button was clicked).
{% figure %}

{% /figure %}
Use this tool with caution. It is possible to introduce post-assignment data into your assignment data, biasing results. For this reason, Statsig disables qualifying event filters by default on Assign and Analyze experiments, because the Statsig SDK typically doesn't over-expose units. Contact the Statsig team to enable this feature if you have a use case for it.
The available settings are:
- Qualifying Event: the event source to qualify exposures with. You can filter this to pick specific target events within a qualifying event source.
- Exclude matching units: whether to include or exclude units that triggered the event.
- Use qualifying event timestamp for first exposures: if the actual intervention occurred when the unit triggered the qualifying event, enable this setting so that Statsig overrides the exposure timestamp with the qualifying event timestamp.
- Filter events by time window: restrict qualifying event matching to events that occurred within X days or minutes of the unit's exposure event.
## Filter Assignment Source
You can also filter exposures to units based on columns in the assignment source. You can use certain columns directly and apply additional filters on top.
As with filtering by qualifying event, use this tool carefully because it can artificially introduce bias to experiment results.
{% figure %}
{% /figure %}
================================================================================
# Normalized Metrics (aka Clustered Experiments)
URL: https://docs.statsig.com/statsig-warehouse-native/metrics/normalized-metrics
Updated: 2026-07-11
================================================================================
## When to use normalized metrics
In standard A/B tests, the unit of randomization (for example, UserID) matches the unit of analysis. In some cases it is useful for these to be different. The most common example is B2B experiments where you want to randomize by BusinessID but measure by average metric per UserID, not average metric per BusinessID.
For example, if you add image support to a collaborative commenting feature and want to A/B test it before rollout, you randomize using BusinessID. You can't randomize by UserID, because everyone within a single business must have the same treatment. If you compared comments per BusinessID, the data would be skewed by large companies. A business with 1000 employees and 10 comments would contribute more weight than a business with 5 employees and 5 comments. Normalizing the metric by users exposed to the experiment corrects for this: if 1000 and 5 users were exposed from each business, the first business would have a comments/user rate of 0.01 and the second would have a rate of 1, making the comparison valid across companies of different sizes.
## What normalized metrics do
Internally, normalizing a metric creates a ratio metric. The numerator is the metric being normalized. The denominator is a COUNT DISTINCT of the UnitID being normalized to.
To create this ratio metric yourself and use it in experiments, refer to the [Cluster Experiments](/metrics/different-id) guide.
## How to create normalized metrics
Create normalized metrics when adding metrics to an experiment. 
Select the unitID to normalize by on the next screen.
## Where to use normalized metrics
Normalized metrics are available anywhere you can run a ratio metric and a count-distinct metric.
================================================================================
# Experiment Quality Score
URL: https://docs.statsig.com/statsig-warehouse-native/features/quality-score
Updated: 2026-07-11
================================================================================
## How Experiment Quality Score works
The Experiment Quality Score is a metric that gives a quick view of the quality and trustworthiness of an experiment configured in Statsig.
The score helps experimenters and their peers across an organization identify potential issues in experiment setup, execution, and data collection, supporting more confident decision-making. Measuring this score across all experiments can help teams discover systematic issues in their program and identify opportunities to improve their experimentation program over time.
## Configure Experiment Quality Score
Enable Experiment Quality Score in the project settings under *Settings > Experimentation > Experiment Quality Score*.
The feature uses a list of pre-defined assessment criteria. You can customize the weight of each criterion based on your organization's needs, though Statsig provides default values.
{% figure %}

{% /figure %}
## Advanced configuration using the console API
For teams that need additional checks, have different requirements across product teams, or need different thresholds, Statsig supports advanced configuration through the console API. For example, you can require hypotheses to be at least 200 characters and contain a link to an external planning document.
To configure this, run a POST or PATCH on the `console/v1/experiments` endpoint to update individual scores on any given experiment. Targeting the existing set of scores lets you override weights (usually to 0), so the list contains only the custom set needed.
For example, running patch on an experiment with this payload:
```
{
"manualQualityScores": [
{
"criteriaName": "HYPOTHESIS_LENGTH",
"criteriaDescription": "Check passed",
"status": "PASSED",
"score": 0,
"weight": 0
},
{
"criteriaName": "MyCompany\'s Hypothesis Check",
"criteriaDescription": "Has Internal URL and > 200 Chars",
"status": "PASSED",
"score": 100,
"weight": 100
},
{
"criteriaName": "Naming",
"criteriaDescription": "Experiment prefixed with team name",
"status": "FAILED",
"score": 0,
"weight": 100
}
]
}
```
Would:
* Drop the original HYPOTHESIS\_LENGTH check
* Keep the other original checks, with their weights
* Add a new check, `MyCompany's Hypothesis Check`, for custom logic on the hypothesis
* Add a new check, `Naming`, for custom logic on the name
The other weights are normalized. If the original HYPOTHESIS\_LENGTH had a weight of 10, the total weight becomes 290 and scores are normalized accordingly. If all non-custom checks are passing, the score would be 190/290 or ~66%.
The general flow for using this approach is:
* Use Console API's `experiments/get` to pull all experiments
* For each experiment:
* Run custom logic
* Patch results
## Calculation notes
Statsig skips checks in an unready state during evaluation and renormalizes the other weights to 100%. For example, if the experiment hasn't started, the `Balanced Exposures` component is in an unready state and Statsig ignores it.
Checks with a weight of 0 are omitted entirely from the card.
## View quality scores
When enabled, quality scores appear in the details tab of an experiment. Statsig evaluates applicable checks and contributes them to the number shown.
The score is color-coded based on the percentage threshold:
* >= 85% corresponds to passing/green
* >= 50% corresponds to warning/yellow
* < 50% corresponds to error/red
{% figure %}

{% /figure %}
Quality scores are also available through the console API, which lets you retrieve the data in bulk for analysis.
================================================================================
# Experiment Diagnostics
URL: https://docs.statsig.com/statsig-warehouse-native/features/monitor-an-experiment
Updated: 2026-07-11
================================================================================
After you turn on your experiment, you can monitor its health and view the exposures for the control and variant groups.
### Monitoring experiment health
To monitor the status of your experiment,
* Navigate to **Experiments** in the left-hand navigation panel in the Statsig console
* Select the experiment you want to monitor
* **Experiment Health Checks** show alerts for problems with the experiment setup. Hover over the icon and click on a check for more details.
{% figure %}

{% /figure %}
* **Pulse metrics available** monitors availability of Pulse results, which Statsig expects the day after the experiment starts if your metrics have landed.
* **Exposures are balanced** checks that the number of units exposed in each group matches the expected allocation. The Sample Ratio Mismatch (SRM) check uses a Chi-Squared test of independence. Experiments can temporarily show small imbalances due to the randomness of user assignment; these typically resolve after a couple of days. True SRM imbalances that originate from systematic assignment or logging problems tend to persist over time. The following thresholds apply to SRM alerts:
* **p-value between 0.001 and 0.01**: Warning (yellow) for possible imbalance. The p-value isn't low enough to confirm a real imbalance. Wait and check again the next day.
* **p-value \< 0.01 and group size differs from expected size by less than 0.1% absolute**: Warning (yellow) indicating that an imbalance is possible, but the impact to the experiment is expected to be small. This scenario typically occurs in large web experiments (1M+ users) where small variations in performance across groups can cause a small fraction of exposures to be dropped for certain groups more than others.
* **p-value \< 0.001 and more than 0.1% absolute deviation from expected group size**: Alert (red) meaning there is likely a problem with the experiment exposures and experiment results may not be trustworthy.
* **Crossover users** monitors the percentage of units exposed to more than one experiment group. Statsig discards crossover users from the experiment analysis, which may reduce your sample size and statistical power. The following alerts apply based on the threshold for that experiment type:
* **Warning (yellow)**: The presence of a number of crossover users has been detected, but the impact on the experiment is expected to be relatively small.
* Assign & Analyze: **0.1% to 1%**
* Analyze Only: **1% to 10%**
* **Alert (red)**: There are a substantial number of crossover users, which can indicate a problem with your experiment setup.
* Assign & Analyze: **Over 1%**
* Analyze Only: **Over 10%**
* **User metrics were computed** confirms that Statsig was able to join exposure data to metric data and produce experiment results.
* **Metrics Available for Topline Impact** checks whether there is sufficient metric data to calculate topline impacts. Go to [topline impact](/experiments/statistical-methods/topline-impact) for details.
* **Differential Impact Detection completed** checks whether the differential impact detection has finished and flags extreme differential impacts for any sub-population per your configuration. Go to [differential impact detection](/experiments/exploring-results/differential-impact-detection) for details.
Some health checks apply only to end-to-end Statsig experiments where the Statsig SDK handles assignment; others apply only to analysis experiments where assignment is handled outside of Statsig.
* **Checks started** (end-to-end only) verifies that config checks are occurring. Available shortly after the experiment starts.
* **Checks have valid unit type** (end-to-end only) ensures that config checks contain the unit ID type selected for this experiment (user ID by default). Available when checks begin.
* **Exposures found** (analysis only) verifies that exposures for this experiment are provided, and is populated after pulse is loaded.
Some health checks apply to experiments where ID types are stitched: for example, a sign-up flow experiment where stitching Stable ID to User ID enables analysis before and after a user is logged in.
Duplicate mapped IDs occur when the same primary ID is associated with multiple secondary IDs or vice versa. This happens when the primary identifier is reset, or when there are multiple conversions from the same primary identifier. Deduplication can reduce your sample size and may affect the overall magnitude of your results.
* **Deduplication Rate Check** reports the percentage of exposures which have duplicate mapped ids across primary and secondary IDs. The following thresholds are used for displaying deduplication rate alerts:
* **Deduplication rate between 1% and 5%** Warning (yellow) indicating that deduplications have been applied; affected units are excluded from analysis, which reduces the sample size and may affect the overall magnitude of results.
* **Deduplication rate greater than 5%** Alert (red) indicating that a substantial number of deduplications have been applied and excluded from analysis. This can indicate a problem with the experiment setup or a 1-many mapping between the two IDs.
:::note
Statsig supports 1-many and many-many mapping in Advanced ID Resolution, in addition to 1-1 mapping. Go to [Advanced ID Resolution](/statsig-warehouse-native/features/id-resolution) for details.
* **Deduplication Bias Check** reports the percentage of exposures which have duplicate mapped ids across primary and secondary IDs in each group and performs a Chi-Squared test of independence. The following thresholds are used for displaying deduplication bias alerts:
* **p-value between 0.001 and 0.01**: Warning (yellow) indicating possible bias. The p-value isn't low enough to confirm a real imbalance in deduplication rate between groups. Wait and check again the next day.
* **p-value \< 0.001**: Alert (red) indicating that Statsig is likely applying deduplication more to one treatment group, and experiment results may not be trustworthy.
* **Pre-experimental Bias Check** In some cases, users in two experiment groups have meaningfully different average behaviors before the experiment applies any intervention. If this difference persists after the experiment starts, the analysis may attribute that pre-existing difference to the intervention. CUPED helps address this bias but can't fully account for it. [Learn more](/experiments/statistical-methods/pre-experiment-bias).
* **Outlier Check** In some cases, metrics can be materially influenced by a few outliers. This check alerts you so you can evaluate whether tools like Winsorization or Capping are appropriate. It also verifies that capping isn't unduly influencing results if already applied.
================================================================================
# Loading Pulse
URL: https://docs.statsig.com/statsig-warehouse-native/features/reloads
Updated: 2026-07-11
================================================================================
## How the Pulse Engine works
Statsig's experimentation engine runs the setup, diagnostics, and transformations required to generate the data points that power statistical experiment analysis. In Warehouse Native, this consists of a series of query jobs (referred to as a DAG, or Directed Acyclic Graph) that take data from your sources and metric configurations to a final result set.
You can find reload controls on the pulse results page:
{% figure %}

{% /figure %}
### Types of reloads
Statsig offers a number of ways to reload data:
* [Full Reloads](/statsig-warehouse-native/features/full-reloads) completely restate your experiment data. This can be useful if your underlying data changes a lot (e.g. a full DBT reload) day-to-day, and you want to ensure your analysis matches your raw data.
* [Incremental Reloads](/statsig-warehouse-native/features/incremental-reloads) update your data from the last load to the current date. Running daily incremental reloads is the recommended way to keep your data current without using unnecessary compute resources to recalculate data that hasn't changed.
* [Metric Reloads](/statsig-warehouse-native/features/metric-reloads) are a useful feature for when you want to add a metric to an analysis, or when a metric definition has changed. This does an efficient spot replacement of the data for a single metric or set of metrics.
You can [schedule](/statsig-warehouse-native/connecting-your-warehouse/scheduled-reloads/) full and incremental loads to get fresh results each day.
### Transparency
For every load, Statsig logs the compute time and jobs, cost, and queries associated with the reload. This information is visible in your console and helps you understand what is taking time or delaying results.
### Efficient reloads
Statsig optimizes the queries running in your warehouse. In head-to-head evaluations, customers report that Statsig uses significantly fewer resources than comparable platforms.
Statsig also offers turbo mode, which skips some enrichment calculations (in particular some time series rollups) to compute the latest snapshot of your data at lower cost. Using turbo mode, customers have run experiments on 150+ million users in less than 5 minutes on a Snowflake S cluster.
### Cleaning up storage
Statsig automatically cleans up explore datasets, power analyses, and stratification artifacts. After you make a decision on an experiment, you can choose to delete the staging datasets, the result datasets, or both. You can also return to the experiment later and clean up from the experiment menu.
================================================================================
# Turbo Mode
URL: https://docs.statsig.com/statsig-warehouse-native/features/turbo
Updated: 2026-07-11
================================================================================
By default, Statsig and other warehouse-native platforms calculate cumulative results for every day in an experiment. This lets users review a rich historical view or make changes and see how those changes would have affected an analysis on previous days.
This is a heavily optimized flow, but it does require additional compute. Turbo Mode was built to give customers control of the tradeoff between compute cost and historical tracking, particularly for large experiments.
## How to use turbo mode
When loading Pulse, there is a checkbox to enable Turbo Mode. Subsequent scheduled loads use the last setting. Turbo Mode changes the shape of the underlying data, so switching back to standard analysis requires a full reload with Turbo Mode disabled.
There is also a project-level setting for Turbo Mode being on or off by default in a project's experimentation settings.
## What Turbo Mode removes
Turbo Mode removes two features: the ability to use the date picker to view Pulse results as of a historical date, and the cumulative timeseries.
Both features are available when an experiment is loaded daily using incremental reloads. However, they may miss dates if there are gaps in the loading schedule, and they don't retroactively update when you run a full reload.
{% figure %}

{% /figure %}
## What Turbo Mode keeps
Turbo mode and standard loads both calculate identical pulse results (including CUPED and other advanced techniques) for the latest day of the experiment analysis, and Turbo mode still calculates the "daily" and "days since exposure" timeseries for diagnosis, as well as pre-experiment bias checks.
## What to expect
Results vary depending on experiment settings, but Turbo jobs on large, long-running experiments typically show a 40-80% reduction in load time and compute cost. This estimate is likely biased and treat it as approximate, because the observation comes from cases where standard loads were slow and Turbo Mode was applied in response. There is no good counterfactual for companies that use Turbo Mode by default.
## When to use Turbo
Turbo Mode is more effective for long-running experiments using full reloads, because without Turbo Mode the job must recreate the entire history of "user state" on each day. Turbo Mode calculates statistics only for the latest snapshot, skipping many calculations.
Turbo Mode is also more effective for experiments with an unusually large number of users or metrics, because it reduces memory requirements and prevents the warehouse from spilling to disk. Disk spill dramatically slows jobs and increases cost.
Holdouts benefit from Turbo Mode because they typically have a large battery of metrics, expose a large portion of a project's users, and commonly run for 3-6 months.
Turbo Mode is less effective for experiments with scheduled incremental reloads or for smaller experiments.
================================================================================
# Full Reloads
URL: https://docs.statsig.com/statsig-warehouse-native/features/full-reloads
Updated: 2026-07-11
================================================================================
Full reloads completely wipe the Staging/Results Datasets Statsig has used for previous pulse calculations, and Statsig recalculates results from scratch.
Generally, use full reloads for
* Initial or historical pulse loads.
* Cases where data has been lost or dropped on the customer side, meaning incremental reloads have lost state.
* Cases where data changes frequently, e.g. a DBT full reload changes historical data due to chargebacks, model changes, or other reasons.
* There's complex data dependencies, and a team wants to ensure that the gap between Statsig and Internal Systems doesn't cause inconsistencies.
================================================================================
# Incremental Reloads
URL: https://docs.statsig.com/statsig-warehouse-native/features/incremental-reloads
Updated: 2026-07-11
================================================================================
Incremental reloads save state from the last load, and load from the latest data read with a small buffer to ensure completeness. This job wipes data since the last load, plus that buffer, and then appends all new data to the staging datasets before calculating results for changed days.
This is the recommended way to load active experiments, and works best for ongoing, daily loads, especially when datasets are large.
================================================================================
# Metric Reloads
URL: https://docs.statsig.com/statsig-warehouse-native/features/metric-reloads
Updated: 2026-07-11
================================================================================
Metric reloads drop all data from staging pipelines associated with a metric and restate that data from scratch. Where this data is interconnected (for example, ratios and funnels), related entities are updated as well.
This saves time when you need to add a new metric to an experiment or a metric definition has changed, because you can avoid reloading unrelated experiment metrics.
================================================================================
# Scheduled Reloads
URL: https://docs.statsig.com/statsig-warehouse-native/connecting-your-warehouse/scheduled-reloads
Updated: 2026-07-11
================================================================================
## How scheduled reloads work
You can control daily reload settings for Metrics and Experiments. Configure these settings on each entity, or set project-level defaults that entities inherit from in your Project Settings. You can also use the Console API to [trigger experiment result loads](/console-api/introduction#post-/experiments/-experiment_id-/load_pulse). This is useful for triggering refreshes when your data pipelines are ready.
{% figure %}
{% /figure %}
{% figure %}

{% /figure %}
## Gate/experiment Pulse scheduling
For feature gates and experiments, individual Pulse scheduling is available separately from the project-level settings. Schedule daily Pulse metric reloads for individual feature gates and experiments.
To access this feature:
1. Navigate to your feature gate/experiment in the Statsig Console
2. Go to the Pulse Results tab
3. Use the scheduling controls to configure daily reloads
The scheduling allows you to:
* Set a daily reload time in UTC
* Choose between Full and Incremental reload types
* Save, edit, or cancel scheduled reloads
This feature requires gates with partial rollout rules and overrides project-level settings.
================================================================================
# Pipeline Overview
URL: https://docs.statsig.com/statsig-warehouse-native/analysis-tools/pipeline-overview
Updated: 2026-07-11
================================================================================
This page gives a high-level overview of the pipeline Statsig Warehouse Native runs on your warehouse.
## Main steps
The main steps in the pipeline are:
* Identifying users' first exposures
* Annotating Metric Sources with exposure data
* Creating metric-user-day level staging data
* Running intermediate rollups for better performance
* Calculating group-level summary Statistics
{% figure %}

{% /figure %}
## Types of DAGs
Statsig lets you run your pipeline in a few different ways:
* A **Full Refresh** totally restates the experiment's data and calculates it from scratch. This is useful for starting an experiment, or if underlying data has changed
* An **Incremental Refresh** appends new data to your experiment data. This reduces the cost of running scheduled updates to your results
* A **Metric** refresh allows you to update a specific metric in case you changed a definition, or want to add new metrics to your analysis
## Artifacts and entity relationships
Statsig generates and stores the following tables in your warehouse per-experiment. You have full access to these data sources for your own analysis, models, or visualizations. For experiments, `experiment_id` is the name of the experiment; for Feature Gates, `experiment_id` is the name of the gate along with the specific rule ID (for example, `chatbot_llm_model_switch_31e9jwlgO1bSSznKntb2gp_exposures_summary`)
This is not an exhaustive list, but it includes most of the core result and staging tables useful for your own analysis.
{% callout type="note" %}
These are internal tables and will change as the product evolves. Changes will be documented here.
{% /callout %}
| Table | Description | Notes |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| `first_exposures_` | Deduplicated and stitched (for experiments with ID resolution) first exposure events | Useful for ad-hoc analysis |
| `exposures_summary_` | Timeseries of exposures per group for display in Pulse | |
| `unit_day_metrics_` | User-day level metric aggregations table | Useful for ad-hoc analysis |
| `unit_covariate_metrics_` | User-level pre-experiment aggregations for regression adjustment/CUPED | |
| `funnel_events_` | Staging table for running funnel analysis | |
| `percentile_values_` | Staging table for running percentile analysis | |
| `distinct_values_` |Staging table for running count distinct analysis | |
| `windowed_metrics_` | Staging table for generating running totals when restating Pulse | |
| `ratio_aggregations_` | Staging table for generating running totals when restating Pulse | |
| `results__` | Outputs of Statistical Analysis for different rollups (e.g. daily, days-since-exposure, cumulative, 7-day). Exported to Statsig | Pulse inputs - useful for replicating Statistical analysis |
| `ratio_results__` | Outputs of Statistical Analysis for ratio metrics in different rollups (e.g. daily, days-since-exposure, cumulative, 7-day). Exported to Statsig | Pulse inputs - useful for replicating Statistical analysis |
The high-level relationships and contents of these tables appear in the entity relationship diagram below. Refer to the Main steps diagram above for scheduling details.
{% figure %}

{% /figure %}
## Other jobs
Alongside and inside this main flow, Statsig also:
* Run Health Checks and a Summary View for exposures
* Calculate top dimensions for dimensional metrics
* Calculate funnel steps
* Run CUPED and Winsorization procedures during the group-level summaries to reduce variance and outlier influence
* Calculate inputs to the Delta Method to avoid bias on Ratio and Mean metrics
Statsig generates experiment-level tables, making it easy to run follow-up analyses on specific experiments.
## Visibility
Select the history icon on your pulse results to see the jobs and IDs Statsig ran for each pulse reload, along with compute time and cost.
This information is also visible in your warehouse's own history and usage management. Having the costs in the console is useful for cross-functional experimentation teams running the analysis.
## Exposure export table
Statsig dedupes and records production exposures into the forwarded exposures table configured in your warehouse Data Connection. This table contains each user's first exposure to an experiment. For feature gates, Statsig dedupes and records exposures for partial rollouts (for example, 5% or 50% rollouts, but not 0% or 100% rollouts).
| Column Name | Data Type | Description |
| ----------------------- | --------- | --------------------------------------------------------------------------------------------- |
| experiment\_id | string | The identifier for the gate/experiment |
| group\_id | string | groupID for experiments; ruleID+Pass/Fail for gates |
| group\_name | string | Name of the experiment group (e.g. Control vs Test) |
| user\_id | string | The ID passed in as the Statsig userID |
| stable\_id | string | Statsig Client SDK managed stable device identifier |
| \[your custom ids] | string | One column for every custom unitID you use on Statsig |
| timestamp | timestamp | Timestamp of the first exposure |
| user\_dimensions | object | Warehouse specific object with all the user dimensions |
Statsig populates `user_dimensions` in the daily deduplicated export. Fast-forwarded exposure rows can omit some fields in this object until the next daily load.
### Common fields in user\_dimensions
`user_dimensions` contains user attributes captured alongside the first exposure. The exact shape can vary by SDK and project configuration, but these are some of the most common fields you may see:
| Field | Description | Notes |
| ---------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------- |
| `os` | Normalized operating system name | Canonical OS field on the exposure side. |
| `os_version` | Operating system version | Derived from SDK metadata or user agent parsing. |
| `browser_name` | Browser name | Derived from SDK metadata or user agent parsing. |
| `browser_version` | Browser version | Derived from SDK metadata or user agent parsing. |
| `device_model` | Device model | Forwarded or inferred when available. |
| `ip` | IP address | Present when available from the SDK or request context. |
| `country` | Country | Derived from request context or IP lookup. |
| `locale` | Locale | Forwarded or inferred when available. |
| `language` | Language | Forwarded or inferred when available. |
| `appVersion` | Application version | Forwarded when present on the SDK user object. |
| `sessionID` | Session identifier | Forwarded when present on the SDK user object. |
| `appIdentifier` | Application identifier | Forwarded when present on the SDK user object. |
Additional non-null fields from the SDK user object may also appear in `user_dimensions`. Custom IDs are typically exported as dedicated top-level columns in the exposure table rather than being queried from this object.
Statsig uses input fields such as `deviceOS` and `systemName` to derive the exported `os` field. If you want to analyze operating system on forwarded exposures, query `user_dimensions.os`.
## Event export table
If you log custom events through a Statsig SDK, Statsig also forwards those events into a configurable table in your warehouse. This is the table used when Warehouse Native customers rely on Statsig SDK logging for outcome events.
* Use `user_object` for user fields associated with the event.
* Use `statsig_metadata` for SDK and exposure-processing metadata.
* Use `company_metadata` for the event metadata payload you logged.
| Column Name | Data Type | Description |
| ----------------------- | --------- | --------------------------------------------------------------------------------------------- |
| user\_id | string | The ID passed in as the Statsig userID |
| stable\_id | string | Statsig Client SDK managed stable device identifier |
| \[your custom ids] | string | One column for every custom unitID you use on Statsig |
| timestamp | timestamp | Event timestamp |
| event\_name | string | Name of the logged custom event |
| event\_value | string | Optional event value |
| user\_object | object | Warehouse specific object containing user fields associated with the event |
| statsig\_metadata | object | Warehouse specific object containing Statsig SDK and exposure metadata |
| company\_metadata | object | Event metadata payload logged with the event |
### Common fields in user\_object
`user_object` contains user fields associated with the event. It often includes the same common fields as `user_dimensions`, plus any additional non-null fields sent on the SDK user object.
| Field | Description | Notes |
| ---------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------- |
| `os` | Normalized operating system name | Canonical OS field on the event-side user object. |
| `os_version` | Operating system version | Derived from SDK metadata or user agent parsing. |
| `browser_name` | Browser name | Derived from SDK metadata or user agent parsing. |
| `browser_version` | Browser version | Derived from SDK metadata or user agent parsing. |
| `device_model` | Device model | Derived from `deviceModel` when provided. |
| `ip` | IP address | Present when available from the SDK or request context. |
| `city` | City | Added when geographic inference is available. |
| `state` | State or region | Added when geographic inference is available. |
| `country` | Country | Derived from request context or IP lookup. |
| `locale` | Locale | Forwarded or inferred when available. |
| `language` | Language | Forwarded or inferred when available. |
| `appVersion` | Application version | Forwarded when present on the SDK user object. |
| `sessionID` | Session identifier | Forwarded when present on the SDK user object. |
| `appIdentifier` | Application identifier | Forwarded when present on the SDK user object. |
### Common fields in statsig\_metadata
`statsig_metadata` contains SDK-level and exposure-processing metadata associated with the event. These are some of the most common customer-facing fields:
| Field | Description | Notes |
| ---------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------- |
| `deviceType` | High-level device category | Derived from the normalized OS, for example `Desktop` or `Mobile`. |
| `targetAppID` | Target app identifier | SDK target app metadata. |
| `statsigTier` | Statsig environment tier | For example `prod` or `staging`. |
| `keyEnvironment` | SDK key environment | Environment associated with the SDK key. |
| `keyID` | SDK key identifier | Useful for debugging ingestion and environment issues. |
| `samplingRate` | Event sampling rate | Present when the event is sampled. |
| `is_bot` | Bot classification | Set when Statsig classifies the event as bot traffic. |
| `billing_type` | Exposure billing classification | Common on exposure-related events. |
| `groupID` | Experiment group identifier | Common on exposure-related events. |
| `ruleID` | Rule identifier | Common on exposure-related events. |
| `continuous_rollout_id`| Continuous rollout identifier | Present for continuous rollout exposures. |
| `configExposureType` | Exposure config type | For example `dynamic_config` or `experiment`. |
| `isSwitchback` | Switchback flag | Present when the exposure is associated with a switchback experiment. |
| `is_autotune` | Autotune flag | Present when the exposure is associated with an autotune experiment. |
Other SDK debugging and exposure-processing metadata may also appear in `statsig_metadata`.
### What goes in company\_metadata
`company_metadata` stores the metadata payload logged with the event. This object doesn't have a fixed schema and varies based on the event and your SDK usage. Most event-specific business context, such as `price`, `currency`, `category`, `plan`, `screen`, `route`, or nested objects like `cart`, `items`, and `context`, will appear here.
================================================================================
# Early Diagnostics
URL: https://docs.statsig.com/statsig-warehouse-native/features/freshness
Updated: 2026-07-11
================================================================================
## Exposures from Statsig SDKs
When Pulse is loaded, Statsig performs a just-in-time update of exposures in your warehouse for the first 1 million exposures per experiment. After that threshold, updates occur on a daily cadence.
After launching an experiment, you may want to check certain results (such as page load time, impressions on key pages, and business-critical metrics) as early as possible to detect crashes or bugs.
If you are using the Statsig SDK to generate exposures, Statsig updates exposures in your warehouse before computing Pulse results when Pulse is loaded soon after the experiment starts. This lets you see Pulse results **as fresh as approximately 15 minutes** (assuming events and metrics arrive at the same speed).
After the first 1 million exposures, Statsig batches, deduplicates, and writes exposures to your warehouse once a day. The daily job also includes additional fields that the just-in-time write doesn't include (for example, Group Names).
## Custom Events from Statsig SDKs
Statsig exports custom event logs to your warehouse hourly, plus a short processing delay. You can see Pulse results on metrics derived from those events **as fresh as approximately 1 hour**.
================================================================================
# Read Results
URL: https://docs.statsig.com/statsig-warehouse-native/features/interpreting-results/read-results
Updated: 2026-07-11
================================================================================
## Read experiment results
To read the results of your experiment, go to the **Results** tab, where you can see your experiment hypothesis, **Exposures**, and **Scorecard**.
### Exposures
{% figure %}

{% /figure %}
At the top of the Results page is the Exposures Chart. Exposures are the unique experimental units enrolled in the experiment. This is typically the number of unique users; for device-level experimentation, this is the number of devices. The timeline shows when the experiment started and how many exposures were enrolled on each day. Use the chart to see the rate at which users were added into each group, the total number of users exposed, and whether the target ratio matches what you configured in experiment setup.
### Scorecard
The experiment **Scorecard** shows the metric lifts for all Primary and Secondary metrics you set up at experiment creation.
#### Immediately post-experiment start
For up to the first 24 hours after starting your experiment (before the daily metric results run), the **Scorecard** section is calculated hourly. This applies to Statsig Cloud only; for WHN projects you must reload results on demand or set up a daily schedule. This near-real-time scorecard lets you confirm that exposures and metrics are calculated as expected and debug your experiment or gate setup if needed.
{% callout type="note" %}
Do **not** make experiment decisions based on real-time results data in this first 24-hour window after experiment start. Make decisions only after the experiment has reached its target duration, as determined by your primary metric(s) reaching experimental power.
For more about target duration, go to [target duration](/experiments/create-new#target-duration).
{% /callout %}
Because data in this early post-experiment window is intended for diagnostics rather than decision-making, there are a few key differences from the results shown after daily runs begin:
- Metric lifts don't have confidence intervals
- No time-series view of metric trends
- No projected topline impact analysis
- No option to apply more advanced statistical tactics, such as CUPED or Sequential Testing
All of these are available in daily Results, which start showing in the next daily run.
#### Post-first day scorecard
{% figure %}

{% /figure %}
The experiment Results daily run calculates the difference between comparable randomization groups (for example, test and control) across your organization's suite of metrics, and applies a statistical test to the results. For more about Statsig's stats engine, go to [the stats engine documentation](/experiments/statistical-methods/introduction).
For every metric, Statsig shows:
- The calculated relative difference (Delta %)
- The confidence interval
- Whether the result is statistically significant
- Positive lifts are green
- Negative lifts are red
- Non-significant results are grey
The formula for calculating lift is:
Delta(%) = (Test - Control) / Control
Confidence intervals are reported at the selected significance level (95% by default). In a typical two-sided Z-test, Statsig shows the confidence interval as +/- 1.96 \* standard error.
Statsig automatically applies 99.9% winsorization to event_count, event_count_custom, and sum metrics. This caps extreme outlier values to reduce their impact on experiment results. For metrics added to the **Scorecard** or **Monitoring Metrics** sections of your experiment or gate, you can also apply optional statistical treatments, such as CUPED (pre-experiment bias reduction) and sequential testing adapted confidence intervals. For more details, go to [the stats engine documentation](/experiments/statistical-methods/introduction).
- **Experiment results are computed for the first 90 days**: By default, Statsig computes experiment results for only the first 90 days of your experiment. You receive an email notification as you approach the 90-day limit, at which point you can extend the compute window by another 30 days at a time. If the experiment runs beyond the compute window, Statsig stops adding new users to the experiment's results, but analysis for existing exposed users continues until you make a decision on the experiment.
{% callout type="note" %}
This experiment result calculation window only affects whether a user is included in the experiment's analysis, and doesn't affect the treatment each user receives. New users still receive the experience for the group they get randomized into.
{% /callout %}
### Experiment results views
The following views are available for Scorecard metric lifts:
- **Cumulative results (default view)**: Displays the aggregate difference between experiment groups and visualizes the corresponding confidence intervals.
- **Table view**: Displays the same data as the cumulative view but in a table format with additional fields.
- **Daily results**: Shows the difference between experiment groups aggregated based on days since start of experiment.
- **Days since exposure**: Shows the difference between experiment groups aggregated based on days since exposure to the experiment.
Cumulative results includes a detailed view on hover, where you can additionally view the raw statistics used in the metric lift calculations, as well as topline impact.
{% figure %}

{% /figure %}
### Dimensions
There are two ways to break down a Scorecard metric: by a **User Dimension** or by an **Event Dimension**.
#### User dimensions
User Dimensions refer to user-level attributes that are either part of the user object you log, or additional metadata that Statsig extracts. Examples of these user attributes include operating system, country, and region.
You can create [custom "explore" queries](/experiments/interpreting-results/custom-queries) to _filter on_ or _group by_ available user dimensions. For example, view results for users in the US, or results for iOS users grouped by country. Go to the "explore" tab to create a custom query.
{% figure %}

{% /figure %}
#### Event dimensions
Event Dimensions refer to the value or metadata logged as part of a custom event used to define the metric. To view results for a metric broken down by categories specific to that metric, [specify the dimension](/metrics/metric-dimensions) in the **value** or **metadata** attributes when you log the source event. For example, when you log a "click" event on your web or mobile application, you can log the target category using the **value** attribute. Statsig automatically generates results for each category in addition to the top-level metric.
To see breakdowns for all categories within a metric, click on the (+) sign next to the metric.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
### Significance level settings
You can adjust these settings at any time to view Scorecard results with different significance levels.
- **Apply Benjamini-Hochberg Procedure per Variant**: Select this option to reduce the probability of false positives by adjusting the significance level for multiple comparisons. Go to [Benjamini-Hochberg Procedure](/experiments/statistical-methods/methodologies/benjamini-hochberg-procedure) for details.
- **Confidence Interval**: Changes the confidence interval displayed with the metric deltas. Choose a lower confidence interval (for example, 80%) when there is higher tolerance for false positives and fast iteration with directional results is preferred over longer experiments with greater certainty.
- **CUPED**: Toggle CUPED on or off using the inline settings above the metric lifts. This setting applies only to **Scorecard** metrics; Statsig doesn't apply CUPED to non-Scorecard metrics.
- **Sequential Testing**: Applies a correction to p-values and confidence intervals to reduce false positive rates when evaluating results before the target completion date of the experiment. This mitigates the increased false positive rate associated with the "peeking problem". Toggle **Sequential Testing** on or off using the inline settings above the metric lifts. This setting is available only for experiments with a set target duration.
{% figure %}

{% /figure %}
### Restarting results
{% figure %}
{% /figure %}
If your experiment has stopped computing results, you can resume updates by selecting the Restart button. Before restarting, review the following:
- A Restart isn't a [Reset](/experiments/ending/ending-experiment#stopping-an-experiment). A Restart doesn't re-randomize units in your experiment, and all users continue to receive the same group assignments.
- Statsig begins computing experiment results from the restart point, so metric results start over. Old results may still be available in time series and explore query views, but Statsig doesn't carry them forward or update them.
- The Cumulative Exposures chart updates based on new exposures, but the duration of the pause in computations affects whether the chart starts from zero or retains past exposure counts.
Avoid restarting results by actively extending experiments while they are running. Monitor email alerts from Statsig and check your experiments regularly.
================================================================================
# Metric Drill-Down
URL: https://docs.statsig.com/statsig-warehouse-native/features/interpreting-results/metric-drill-down
Updated: 2026-07-11
================================================================================
## Metric tooltip
A tooltip with key statistics and additional detail appears when you hover over a metric in Pulse.
{% figure %}

{% /figure %}
* **Group**: The name of the group of users. For Feature Gates, the "Pass" group is considered the test group while the "Fail" group is the control. In Experiments, these are the variant names.
* **Units**: The number of distinct units included in the metric. E.g.: Distinct users for user\_id experiments, devices for stable\_id experiments, etc.
* **Mean**: The average per-unit value of the metric for each group.
* **Total**: The total metric value across all units in the group, over the time period of the analysis.
### Calculation details
| Metric Type | Total Calculation | Mean | Units |
|------------------|-------------------------|----------------|----------------|
| event\_count | Sum of events (99.9% winsorization) | Average events per user (99.9% winsorization) | All users |
| event\_user | Sum of event DAU (distinct user-day pairs) | Average event\_dau value per user per day. Statsig calls this "Event Participation Rate" because it represents the probability a user is DAU for that event. | All users |
| ratio | Overall ratio: sum(numerator values)/sum(denominator values) | Overall ratio | Participating users |
| sum | Total sum of values (99.9% winsorization) | Average value per user (99.9% winsorization) | All users |
| mean | Overall mean value | Overall mean value | Participating users |
| user: dau | sum of daily active users | Average metric value per user per day. The probability that a user is DAU | All users |
| user: wau, mau\_28day | Not shown | Average metric value per user per day. The probability that a user is xAU | All users |
| user: new\_dau, new\_wau, new\_mau\_28day | Count of distinct users that are new xAU at some point in the experiment | Fraction of users that are new xAU | All users |
| user: retention metrics | Overall average retention rate | Overall average retention rate | Participating users |
| user: L7, L14, L28 | Not shown | Average L-ness value per user per day | All users |
### p-value
In null hypothesis significance tests, the p-value is the probability that such an extreme difference arises by random chance when the experiment has no effect. A low p-value means the observed difference is unlikely to be due to random chance. In hypothesis testing, a p-value threshold determines which results reflect a real effect and which are plausibly due to random chance. ([p-value calculation](/experiments/statistical-methods/p-value))
### Reverse power
Reverse power is the smallest effect size that an experiment can reliably detect in its current state (some studies refer to this value as ex-post MDE). Statsig calculates it from the sample size and standard error of the control group. Reverse power does *not* depend on the observed effect size. In practice, reverse power answers questions such as: given how the test played out, what is the smallest effect detectable with sufficient power (typically 80%)?
For a two-sided test, the reverse power for a given metric X is computed using the following equation:
$$
Reverse Power = \frac{(Z\_\{1-\beta} + Z\_\{1-\alpha/2})}{\overline{X}\_\{\text{control}}}\times \sqrt{\frac{\mathrm{var}(\Delta \overline{X})}{N\_\{\text{control}}}} \times 100\\%
$$
For a one-sided test, the reverse power for a given metric X is computed using the following equation:
$$
Reverse Power = \frac{(Z\_\{1-\beta} + Z\_\{1-\alpha})}{\overline{X}\_\{\text{control}}}\times \sqrt{\frac{\mathrm{var}(\Delta \overline{X})}{N\_\{\text{control}}}} \times 100\\%
$$
* $\overline{X}\_\{\text{control}}$ is the mean metric value across control users
* $\mathrm{var}(\Delta \overline{X})$ is the population variance of delta
* $N\_\{\text{control}}$ are the observed number of units in the control group
* $Z\_\{1-\beta}$ is the standard Z-score for the selected power. Typically $1-\beta$ = 0.8 and $Z\_\{1-\beta}$ = 0.84
* $Z\_\{1-\alpha/2}$ and $Z\_\{1-\alpha}$ are the standard Z-scores for the selected significance level in a two-sided test and in a one-sided test.
Reverse power is an optional feature. To manage it, go to *Settings > Product Configuration > Experimentation > Organization* and toggle it on or off.
## Detailed view
Click **View Details** to access in-depth metric information. The detailed view contains three sections:
* **Time Series**: How the metrics evolve over time
* **Raw Date**: Group level statistics
* **Impact**: How the experiment impacts the metric
### Time series
In this view, select and drag to zoom in on different time ranges. Three types of time series are available in the drop-down:
**Daily**: The metric impact on each calendar day without aggregating days together. This is useful for assessing day-over-day metric variability and the impact of specific events. This is the recommended time series view for Holdouts, because it highlights the impact over time as new features are launched.
{% figure %}

{% /figure %}
**Cumulative**: Shows the cumulative metric impact from the start of the experiment over time. This is useful for observing trends and seeing how your confidence interval changes over time.
{% figure %}

{% /figure %}
**Days Since Exposure**: Shows the metric impact based on how long a user has been in the experiment. Daily data for each user is aligned by the day they entered the experiment (Day 0, Day 1, etc.), not by calendar date. This lets you distinguish early (novelty) effects from long-term effects. This view also shows pre-experiment data, which identifies biases between groups before the experiment started. Such biases can result from random chance or from an issue in the random assignment process.
{% figure %}

{% /figure %}
### Raw data
This view shows the group-level statistics needed to compute the metric deltas and confidence interval. It includes Units, Mean, and Total (defined in the Metric Tooltip section above), as well as the Standard Error of the mean (Std Err). For details on the statistical calculations, go to the [stats engine documentation](/experiments/statistical-methods/introduction).
### Impact
{% figure %}

{% /figure %}
* **Experiment Delta (absolute)**: The absolute difference of the Mean between test groups i.e. Test Mean - Control Mean. Statsig shows the p-value to indicate whether the observed absolute difference is statistically significant.
* **Experiment Delta (relative)**: Relative difference of the Mean i.e. 100% x (Test Mean – Control Mean) / Control Mean.
* **Topline Impact**: The measured effect that the experiment has on the overall topline metric each day, on average. Statsig computes this daily and averages it across days in the analysis window. The absolute value is the net daily increase or decrease in the metric; the relative value is the daily percentage change.
* **Projected Launch Impact**: An estimate of the daily topline impact expected if you make a decision and launch the test group to all users. This accounts for the layer allocation and the size of the test group. This assumes the targeting gate (if there is one) remains the same after launch.
Go to [topline impact](/experiments/statistical-methods/topline-impact) for details on the exact calculation for topline and projected impact.
**FAQs about topline impact**
*Why is the projected launch impact smaller than the relative experiment delta?*
An experiment may affect only a subset of the user base that contributes to a topline metric. The relative experiment delta is therefore diluted when measured against the topline metric value.
For example: consider a top-of-funnel experiment on the registration page. Among users who visit that page, the treatment leads to more sign-ups and a 10% lift in daily active users (DAU). The topline DAU metric includes other user segments outside the experiment, such as long-term users who don't visit the registration page. A 10% lift in the test vs. control comparison may therefore amount to only a 1% increase in overall DAU.
*How can the topline impact be higher than the experiment delta?*
The topline impact can be higher or lower than the experiment delta because Statsig computes the two values differently and they have different meanings.
Experiment deltas are based on unit-level averages: the mean metric value is computed for each user across all days, then averaged to get the group mean. The topline impact is computed daily based on the total pooled effect from all users, averaged across days to show the daily impact.
Statsig computes topline impacts this way because most metrics are tracked daily and the topline value is typically an aggregation across all users, not a user-level average. For experiment analysis, best practice is to match the analysis unit to the randomization unit, so metrics are aggregated at the unit level before computing experiment deltas.
================================================================================
# Custom "Explore" Queries
URL: https://docs.statsig.com/statsig-warehouse-native/features/interpreting-results/custom-queries
Updated: 2026-07-11
================================================================================
Custom queries run additional experiment analyses on your existing data beyond what is in your main Results tab. Use them to gain deeper insights from experiments and feature rollouts, debug interesting results, or scope results to specific sub-groups. Custom queries let you filter or group metrics by event or user dimensions, or filter to a specific set of users to see how an experiment or launch affected those users.
Custom queries are experimental analyses, and all the same statistical procedures apply as in the main Results tab. Statsig computes results as p-values and confidence intervals for your metric deltas. Advanced statistical methods like [CUPED](/experiments/statistical-methods/methodologies/cuped) and [Sequential Testing](/experiments/advanced-setup/sequential-testing) are also available.
{% callout type="warning" %}
Use caution when drawing inferences from Custom Queries, especially when grouping by a dimension with many options. This increases the chance of a false-positive statistically significant result.
{% /callout %}
### Dimension loading timing for precomputed user dimensions
When viewing results for precomputed user dimensions (which are configured and run on a schedule), Statsig loads these dimensions through separate asynchronous explore queries. This means:
- The main experiment results appear first.
- Precomputed dimensions continue loading in the background and are available within a few minutes.
- This timing gap is most noticeable immediately after the first reload of the day.
- If you see "No dimensions available for this time range" for precomputed dimensions, wait a few minutes and refresh to check whether dimensions have finished loading.
{% callout type="note" %}
This timing behavior only affects precomputed user dimensions that run on a schedule. User-triggered custom queries don't experience this asynchronous loading delay.
{% /callout %}
### Running a custom query
To run a custom query, go to the **Explore** tab within your experiment.
{% figure %}
{% /figure %}
Custom Query fields:
- **Metric(s):** The metric(s) you want to analyze. You can select a single metric, a few metrics, or a Metric Tag. Adding a Tag will include all the metrics within that Tag in your Custom Query. There are three "default" metric selections included as shortcuts:
- "Scorecard Metrics", all metrics included in your experiment setup's Primary and Secondary Metrics sections
- "Primary Metrics"
- "Secondary Metrics"
- **Metric Filter:** With metrics selected, you can filter metrics by either Event or User dimensions using the "Add Filter" dropdown. For example, to view experiment results for Canadian users only, filter to "Country = CA".
{% figure %}

{% /figure %}
- **Group By:** You can group your custom query results by either an Event or User dimension. Whereas custom query filters can be applied at the _per-metric_ level, the Group By action is at the _query_ level (so all included metrics have whatever Group By you select applied to them).
- **Time Range for Metric Data:** The date range you're running your analysis on. By default this is the "Full date range" of your experiment data.
- **(Advanced) ID List Segment filters:** You can choose an ID-list based [Segment](/segments/overview), and your results will only be calculated for users in that segment. This is useful when you did not log an important user dimension to filter on, or when you only need results for a sub-population defined in your own data warehouse.
- This option can easily lead to erroneous and biased results. The segment must be defined based on the user's status _before_ they were exposed to the experiment or feature gate.
- You can also choose to _exclude_ a certain ID list segment. For example, exclude users who have been retroactively identified as bad actors from your lifts analysis.
- **(Advanced) Filter by Exposure Date:** You can also filter the results by Exposure Date for more flexibility. You can include or exclude a date range, or in WHN, include/exclude users based on when they were first exposed to the experiment.
- This is useful when your metrics have a novelty effect, delayed impact, or when you need to filter results to specific users. Use it cautiously because it can lead to biased results.
{% callout type="note" %}
User groups in experiment results are based on first-touch attribution. Filters and grouping are based on the user attributes collected at the time of first exposure in the gate/experiment/layer check.
{% /callout %}
{% figure %}
{% /figure %}
### Viewing a custom query in Explore
These queries take a few minutes to run. After the query completes, Statsig sends an email notification and the results appear in the **Query History** section of the *Explore* interface. All historical queries across your team are stored there. You can assign a display name to a query inline for easier future identification.
{% figure %}
{% /figure %}
### Scheduling a custom query
To get a daily refresh of a custom query, schedule it directly from the **Explore** tab. Create the custom query, then select the "..." menu, then **Schedule**. The custom query runs daily and appears in the **Scheduled** tab of your Metric Lifts.
{% figure %}
{% /figure %}
{% figure %}
{% /figure %}
### Reviewing custom query results
Custom query results resemble the main Results tab because the statistical methods are the same. The key difference is that a custom query result is a snapshot in time. After a query runs, Statsig saves the results and doesn't update them when more metric data is collected. To update results, run a new custom query or schedule custom queries to run on a regular cadence.
#### Sequential testing and custom queries
If [Sequential Testing](/experiments/advanced-setup/sequential-testing) is enabled for your experiment, it applies to custom query results as well. The degree of adjustment to confidence intervals and p-values follows the standard sequential testing rules. If a custom query doesn't satisfy the experiment's target Days or Unique Exposures from your setup, Statsig applies sequential testing adjustments to account for the underpowered state of the experiment.
Because custom queries are snapshots in time, sequential testing adjustments apply to that specific analysis only. Running additional custom queries with more or less data (for example, more days or more unique users) changes the adjustments accordingly. Custom queries that meet the configured minimum Days or Unique Exposures may have no sequential adjustments applied.
{% figure %}

{% /figure %}
================================================================================
# Export Pulse Results to Your Warehouse in Warehouse Native
URL: https://docs.statsig.com/experiments/interpreting-results/access-whn
Updated: 2026-07-11
================================================================================
## How to access Pulse data in Warehouse Native
WHN lets you access exposures and metric results across all experiments directly in your warehouse through SQL Views defined in your Statsig project using a metric source.
### Exposures
Statsig automatically writes exposures to your warehouse to the table configured in your project setup. To find the table's location, go to Settings > Data Connection. The table should be located at the `{Database Name}.{Schema Name}.{Exposures Forwarding Table Name}`, e.g. `experimentation.statsig.exposures`.
### Results
With a SQL View, you have access to experiment metadata (such as experiment team, tags, target duration, and settings like CUPED and Sequential testing), each metric’s metadata (such as metric tags), and all metric lifts: the same set of results visible on the Console. To start using this feature, enable it at **Project Settings > Data Connection > Export**. After enabling it, Statsig automatically sets up the SQL View in your warehouse and creates the metric source in your Statsig project.
Statsig automatically exports scorecard metric results to your data warehouse each time an experiment is loaded, generating a new copy. To differentiate result versions, use the `ds` column, which contains the timestamp when Statsig wrote the data to your warehouse.
{% figure %}

{% /figure %}
### Schema of the results data export table
The default table name used is statsig\_daily\_results. When exports are enabled, Statsig also autocreates a metric source with this name in your Statsig project.
| Column | Type | Description |
|-|-|-|
| ds | timestamp | The time when the data was written at |
| experimentName | string | Name of the experiment |
| experimentCreator | string | Creator of the experiment |
| experimentTeam | string | Team conducting the experiment |
| experimentTags | array of strings | Tags associated with the experiment, represented as an array of strings |
| experimentStartTs | number | Start timestamp of the experiment, in milliseconds |
| experimentEndTs | number | End timestamp of the experiment, in milliseconds |
| targetExposures | number | The target number of exposures for the experiment |
| targetDuration | number | The target duration of the experiment |
| actualDuration | number | The actual duration the experiment ran, from Start Date to Decision Date. For analyze only experiments, this will be TODAY - Configured Start Date |
| controlGroupName | string | Name of the control group in the experiment |
| testGroupName | string | Name of the test group in the experiment |
| useCUPED | boolean | Whether CUPED was applied in the experiment |
| useSequential | boolean | Whether sequential testing was applied in the experiment |
| metricName | string | Name of the metric being measured in the experiment |
| metricType | string | Type of metric |
| metricTags | array of strings | Tags associated with the metric, represented as an array of strings |
| higherIsBetter | boolean | Whether a higher value of the metric is better |
| isVerifiedMetric | boolean | Whether the metric is verified |
| metricTeam | string | Team responsible for the metric |
| absoluteDelta | number | The absolute change in the metric value between control and test groups |
| absoluteDeltaCI | number | Confidence interval for the absolute delta |
| relativeDelta | number | The relative change in the metric value between control and test groups |
| relativeDeltaCI | number | Confidence interval for the relative delta |
| absoluteDeltaPValue | number | P-value associated with the absolute delta metric result |
| toplineAbs | number | The absolute topline metric value for the experiment |
| toplineAbsCI | number | Confidence interval for the absolute topline metric |
| toplineRel | number | The relative topline metric value for the experiment |
| toplineRelCI | number | Confidence interval for the relative topline metric |
| projectedTopline | number | Projected topline metric value based on current data |
| projectedToplineCI | number | Confidence interval for the projected topline metric |
| projectedToplineRel | number | Projected relative topline metric value based on current data |
| projectedToplineRelCI | number | Confidence interval for the projected relative topline metric |
| controlUnits | number | The number of control group units |
| testUnits | number | The number of test group units |
| controlTotal | number | Total value for the control group metric |
| testTotal | number | Total value for the test group metric |
| controlMean | number | The mean value for the control group |
| testMean | number | The mean value for the test group |
| sequentialTestingAbsoluteDeltaCI | number (optional) | Confidence interval for the absolute delta with sequential testing enabled|
| sequentialTestingRelativeDeltaCI | number (optional) | Confidence interval for the relative delta with sequential testing enabled|
| sequentialTestingAbsoluteDeltaPValue | number (optional) | P-value for the absolute delta with sequential testing enabled|
## Report types
There are three types of exports:
1. Exposures - A table of all exposed users and their first exposures. This is useful for joining on your own internal data, and running custom queries within your own data warehouse. You can also use it to verify who was in the experiment, what group they were assigned to, and when they were first exposed (around 1-25MB). This contains:
1. `_first_exposures.csv` - contains a list of users and their first exposure to the experiment.
2. Pulse Summary - This provides precomputed summary experimental data for all metrics and test groups including everything that's visible on Pulse (**around 10-100 kb**). This contains:
1. `_pulse_summary.csv` - contains Pulse aggregate metrics computed over the duration of the experiment.
3. Raw Data - This provides raw exposures and metrics data at the user-day level. This is best used for manually inspecting data, or recomputing your own statistics (**around 10MB-1GB**). This contains:
1. `_first_exposures.csv` - contains a list of users and their first exposure to the experiment. If this is the only file you are interested in, you can get this by exporting an "Exposures" report which is much smaller in size.
2. `_user_metrics.csv` - contains a list of experimental users, and their calculated metrics for each day they were enrolled in the experiment.
In WHN, only the Pulse Summary can be exported, because the other two types of data are only stored [in your warehouse](/statsig-warehouse-native/analysis-tools/pipeline-overview/#artifacts-and-entity-relationships). The availability of these exports is subject to the retention policy. Statsig holds exposures data for up to 90 days after an experiment is concluded, and holds raw user-level metrics data for 90 days.
### Pulse Summary file description - for Feature Gates
| Column Name | Description |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | Name of the Experiment or Feature Gate |
| rule | Name of the Feature Gate Rule. |
| metric\_type | Category of the metric. Different metric\_types are computed differently, including how they're computed in Pulse. |
| metric\_name | The name of the metric. For event metrics, this is the name of the event. |
| metric\_dimension | The subcategory of the metric. For example, if you log value in LogEvent, then value shows up as a subdimension. dimension = !statsig\_topline indicates that this row reflects an aggregate across all dimensions. |
| start\_date | The start date for this measurement |
| end\_date | The end date for this measurement |
| test\_units | The number of users in the test group |
| test\_mean | The average value of this metric across test users (or participating units when applicable) |
| test\_stderr | The standard error for the estimate of the mean for test users. Use this to compute confidence intervals. |
| ctrl\_units | The number of users in the control group |
| ctrl\_mean | The average value of this metric across control users (or participating units when applicable) |
| ctrl\_stderr | The standard error for the estimate of the mean for control users. Use this to compute confidence intervals. |
| abs\_delta | The absolute difference between the test and control mean (test\_mean - ctrl\_mean) |
| abs\_stderr | The estimated standard error of abs\_delta |
| rel\_delta | The relative difference between test and control mean, sometimes referred to as lift (test\_mean - ctrl\_mean)/ctrl\_mean |
| rel\_stderr | The estimated standard error of rel\_delta (abs\_delta/ctrl\_mean) |
| z\_score | The calculated Z-score |
### Pulse Summary file description - for experiments
| Column Name | Description |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | Name of the Experiment or Feature Gate |
| rule | Name of the Feature Gate Rule. |
| experiment\_group | The group of users for which this metric is computed for. For a feature gate, this is pass/fail. For an experiment, this is the variant name. |
| metric\_type | Category of the metric. Different metric\_types are computed differently, including how they're computed in Pulse. |
| metric\_name | The name of the metric. For event metrics, this is the name of the event. |
| metric\_dimension | The subcategory of the metric. For example, if you log value in LogEvent, then value shows up as a subdimension. dimension = !statsig\_topline indicates that this row reflects an aggregate across all dimensions. |
| start\_date | The start date for this measurement |
| end\_date | The end date for this measurement |
| units | The number of users included in this metric estimate. |
| mean | The average value of this metric across units (or participating units when applicable) |
| stderr | The standard error for the estimate of the mean. Use this to compute confidence intervals. |
### First exposures file description
| Column Name | Description |
| ----------------------- | --------------------------------------------------------------------------------------------- |
| unit\_id | Refers to the unit identifier used in the experiment (eg. user\_id, stable\_id, org\_id) |
| name | The name of the gate/experiment |
| rule | For gates, this refers to the rule name |
| experiment\_group | The group the user was assigned to |
| first\_exposure\_utc | The UTC timestamp when the user was first assigned to the experiment |
| first\_exposure\_pst\_date | The date in PST when the user was first assigned to the experiment |
| as\_of\_pst\_date | The date this data was generated |
| user\_dimensions | JSON-formatted key-value pairs describing the user's attributes at the time of first exposure |
### Unit metrics file description
| Column Name | Description |
| ---------------- | ------------------------------------------------------------------------------------------- |
| pst\_ds | The 24hr window the data refers to. All dates are anchored from 12:00a -> 11:59p PST. |
| unit\_id | Refers to the unit identifier used in the experiment (eg. user\_id, stable\_id, org\_id) |
| metric\_type | The category of the metric |
| metric\_name | The name of the metric |
| metric\_dimension | The name of the metric dimension. '!statsig\_topline' is the overall metric with no slicing. |
| metric\_value | The numeric value of the metric |
| numerator | For some metrics, Statsig tracks the numerator |
| denominator | For some metrics, Statsig tracks the denominator |
================================================================================
# Participating Units
URL: https://docs.statsig.com/statsig-warehouse-native/features/interpreting-results/participating-units
Updated: 2026-07-11
================================================================================
## Definition
In Statsig, Participating Units (sometimes referred to as participating users) are a subset of an experiment's total exposed unit count relevant to a particular metric. They count units with a non-zero denominator value for metric types that require a numerator and denominator. These units are used in statistical calculations for ratio-based metrics.
{% figure %}

{% /figure %}
Statsig computes ratio metrics only for users with a non-zero denominator value. For example, a user must trigger the denominator event on a given day to be included in the daily ratio. Statsig excludes users who don't trigger the denominator event during an experiment from the test vs. control comparison of a ratio metric.
## Example
An example of this type of metric for an e-commerce company could be "Total Items Purchased per Order", measuring the average basket size. The numerator would be defined as the count of total items bought, and the denominator would be the total number of orders submitted. If an experiment were run on the checkout flow, only users who actually had at least one Order event would be included in the "Total Items Purchased per Order" ratio metric.
================================================================================
# Best Practices and Avoiding False Positives
URL: https://docs.statsig.com/statsig-warehouse-native/features/interpreting-results/best-practices
Updated: 2026-07-11
================================================================================
Follow these suggestions to interpret Pulse results in a scientifically sound way:
1. Form a hypothesis before viewing Pulse. Identify which metrics you expect to shift, what else could have happened, and what signals indicate something went wrong.
2. Establish a small set of key metrics directly related to your hypothesis. More than a handful of key metrics usually indicates an ill-defined hypothesis or unfocused experimentation. Examining too many metrics increases the false positive rate.
3. Avoid cherry-picking results. Don't selectively pick metrics that look good while ignoring those that don't. Avoid using numbers with no connection to your hypothesis. Statistically significant results should have a plausible explanation (a false positive is a plausible explanation).
4. Multiple independent effects that are consistent with a plausible explanation increase confidence that the observed effects are real, even with borderline p-values.
5. Expect false positives and be cautious about statistically significant results with borderline p-values. A 95% confidence interval (5% significance level) is expected to produce one statistically significant result out of twenty by random chance alone. This rate increases if you include borderline metrics (for example, p = 0.06).
6. Look beyond your hypothesis for additional effects, tradeoffs, and unexpected behaviors. These can reveal information about how users interact with your product and often lead to follow-up experiments.
================================================================================
# Pulse FAQs
URL: https://docs.statsig.com/statsig-warehouse-native/features/interpreting-results/faq
Updated: 2026-07-11
================================================================================
Interpreting statistical results can be difficult, and many users have similar questions. This page covers the most common ones.
## I had a stat sig result, but it turned negative. How should I interpret this
In general, trust the current result, because it incorporates more information about the users in your experiment.
There are several reasons this can happen:
* Random noise, which gets diluted as your sample size gets larger
* Within-week seasonality (e.g. an effect is different on Mondays), which gets normalized with more data
* The users who saw the experiment early are different from slower adopters. A daily user will likely see your experiment before someone who uses your product once a month. Use the time series view for more insight on this.
* A novelty effect made the experiment meaningful early on, but the effect faded. For example, after changing a button, users may click it out of curiosity at first, then revert to prior behavior. Use the days-since-exposure view for more insight on this.
Best practice is to set a readout date when you launch your experiment, based on a [power analysis](/experiments/power-analysis), and to disregard the statistical interpretation of results until that date. Reading results multiple times before the readout date dramatically increases the rate of false positives.
## How should I start interpreting results
Start by using your scorecard metrics to understand whether you moved the metrics you expected to move. Before reviewing Pulse, form a hypothesis about what your experiment should drive. Your primary metrics should answer that hypothesis.
The delta displayed is based on the observed difference between test and control populations. The error bars visualize a confidence interval. A confidence interval is a range of probable values for the difference between groups. A future sample's 95% confidence interval contains the true value 95% of the time. In practice, the CI is a representative range of what the true value might be.
These results are statistical interpretations, not facts:
* If a result is not stat sig, this means you don't have sufficient evidence to reject the null hypothesis (i.e., based on your experiment design the observed result is reasonably likely to have happened by chance).
* Generally, you should treat these results as a lack of evidence for your hypothesis
* Underpowered tests may lead to neutral results even if a true effect exists
* If a result is stat sig, this means that you have sufficient evidence to reject the null hypothesis (i.e., the probability that you would observe this result, or one more extreme, if the two groups' results were identical is below the pre-determined threshold you set).
* Generally, you should treat this result as evidence for your hypothesis
* Multiple comparisons (many metrics, rerunning an experiment, or grouping by dimensions) greatly increase the chance of seeing a stat sig result when there's *not* a true effect. Be wary of interpreting results when you see those behaviors!
* A test that was extremely unlikely to succeed (such as a moonshot) with a stat sig result has a high chance of being a false positive. This is a strong signal, but consider reproducing the result, running a back-test, or reducing your significance level.
After reviewing the scorecard results, use the all-metrics tab and custom queries for more information about your experiment. Examining more metrics increases the chance of a false positive, so a statistically significant movement in those views isn't necessarily a statistically sound interpretation. Use that section to look for unexpected large regressions and to generate follow-up hypotheses.
## Results are missing for some metrics
This typically happens when your organization uses both the SDK or event imports and precomputed metrics imported from your data warehouse. Because these pipelines can run at different times, data availability may differ. Adjust your analysis date range to get a full view of your data.
## Your external source shows more exposure events than Statsig. Are data missing
Statsig doesn't count exposures on the last day (the day you made a decision). Filter out that day when you analyze your external data. The hours that define a "day" for your project depend on the timezone you assigned to your project.
## You log categorical metadata for a custom event, but Pulse doesn't show these breakouts. What's wrong
Pulse shows experimental results for metric sub-groups (for example, iOS vs. Android) only when you configure your metadata as a Dimension. [Value Dimensions](/experiments/interpreting-results/read-results#value-dimensions) are the most common dimension type because their metadata is logged directly with your custom events. Define value dimensions in your [custom event setup](/metrics/metric-dimensions).
## Why do I see "No dimensions available for this time range"
This error appears when you try to view precomputed user dimensions, particularly after the first reload of the day. This happens because:
* Statsig loads dimensions asynchronously in separate explore queries after the main scorecard results load.
* The main experiment results appear first, while dimensions continue loading in the background.
* Dimensions are typically available within a few minutes after the main scorecard loads.
If you encounter this error, wait a few minutes and refresh the page to check whether the dimensions have finished loading.
================================================================================
# Reports
URL: https://docs.statsig.com/statsig-warehouse-native/features/reports
Updated: 2026-07-11
================================================================================
## How report summaries work
Report summaries combine experiment results with context and discussion to produce long-term records of what you learned by running the experiment. Reports live on the Statsig console and can be exported as a PDF to your learnings library.
{% figure %}

{% /figure %}
Reports capture results and in-context discussion, and support embedding media, charts, and rich text formatting for deeper discussion and sharing of follow-ups from outside Statsig.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
================================================================================
# Aggregated Impact
URL: https://docs.statsig.com/statsig-warehouse-native/features/exploring-results/aggregated-impact
Updated: 2026-07-11
================================================================================
## Metric Insights and Aggregated Impact
Statsig's Insights page provides a clear view of how experiments and feature gates affect a specific metric. It helps answer questions such as "How much impact have I driven?" and serves as a tool for diagnosing unexpected changes in metrics.
Insights presents a reverse perspective of the [Pulse](/experiments/interpreting-results/read-results) view. Pulse measures the impact of a new feature on all your metrics; Insights focuses on a single metric and identifies which tests are affecting it the most. This makes Insights useful for assessing your team's impact and setting realistic goals.
## How to read Insights
1. Navigate to the Insights section in the Statsig console: https://console.statsig.com/. Insights is also available in the insight tab for each metric.
2. Select a metric from the drop-down at the top of the page.
3. Select the ID type, time window, and other filters.
4. Based on the filters you choose, you can see the relative impact, topline impact, and projected launch impact for any experiment or gate that uses this metric.
5. Statsig also sums the projected launch impacts, adjusts based on false positive risk ('winner's curse'), and displays the result as the 'Aggregated Impact Estimate'.
{% figure %}

{% /figure %}
## How the math works
Refer to the [topline impact documentation](/experiments/statistical-methods/topline-impact/#computing-projected-launch-impact) for details on how topline and projected launch impact are calculated.
To estimate false positive risk and calculate Aggregated Impact, Statsig uses the methodology in this [paper](https://dl.acm.org/doi/10.1145/3534678.3539160) which is widely adopted across the industry. Specifically:
$$
Aggregated Impact=\sum\_\{i}\{(1 - FPR\_i) \times Projected Launch Impact\_i}
$$
Where the [projected launch impact](/experiments/statistical-methods/topline-impact/) is an estimate of the topline impact assuming a decision is made and the test group is launched to all users; Statsig calculates the false positive risk using the following formula:
$$
FPR\_i = \frac\{\alpha\_i \times \pi}\{\alpha\_i \times \pi + (1 - \beta\_i) \times (1 - \pi)}
$$
In this formula, $\alpha\_i$ is the significance level for experiment i, $\beta\_i$ is the type II error, and 1 - $\pi$ is the prior success rate based on historical experiment results.
================================================================================
# WHN Statistics Overview
URL: https://docs.statsig.com/statsig-warehouse-native/features/statistics
Updated: 2026-07-11
================================================================================
Statsig's statistics engine is designed to deliver high-quality analysis of experiment results. The Stats Engine has four core values: Transparency, Trust, Flexibility, and Power.
The pages below cover the Pulse Statistics Engine in detail, including how Statsig calculates results.
## Transparency
Statsig documents all of its methodology, and the analysis is fully visible in Warehouse Native.
Statsig provides the tools, data, and queries you need to reproduce the results you see in the console, with no hidden logic. Most enterprise data science teams validate results as part of their evaluation process.
Transparency includes:
* Console-based access to the SQL Statsig runs. You have access to the tables and queries on your warehouse, and Statsig surfaces SQL snippets in the console whenever it runs a query so you can verify what is being calculated.
* Visibility into costs. Most customers can run pulse analyses for very low cost, and Statsig surfaces run time and resource utilization to help you manage your warehouse bill for experimentation.
* Support: Statsig offers support with access to the data science team for open discussion of methodology, approaches, and collaborative development of new features.
## Trust
Experiments drive important business decisions, and it's critical that you can trust the analysis and statistics being run. Statsig has a rigorous evaluation process for its methodologies, including peer review, simulations, and publishing the thought process behind statistical designs.
You can trust Statsig's results to be accurate and reliable as they help guide your decisions.
Trust includes:
* Detailed blog posts on the rationale behind decisions in new features
* Documentation of methodologies in blogs, with references to prior art
* A full suite of diagnostic health checks on experiment results to warn you when statistical assumptions or data quality have been compromised
## Flexibility
Experimentation isn't a one-size-fits-all tool. Depending on your industry, philosophy, or the setup of a specific experiment, Statsig lets you configure your analysis to suit your needs, offering:
* Standard T-Tests
* Sequential Testing
* Bayesian Tests
* Switchback Tests
* Multi-armed bandits
Statsig also offers many options to control for multiple comparisons, outliers, and regression adjustment.
## Power
Effective experimentation relies on having trustworthy results quickly. Statsig has invested heavily in accuracy and power, so your results are faster and more reliable.
Examples of features focused on the power of Statsig's stats engine:
* [CUPED](https://www.statsig.com/blog/cuped): reduces experiment run times and accounts for pre-experiment bias
* [Stratified Sampling](/experiments/advanced-setup/stratified-sampling): makes experiment results more accurate and consistent
================================================================================
# Confidence Intervals
URL: https://docs.statsig.com/statsig-warehouse-native/features/statistics/confidence-intervals
Updated: 2026-07-11
================================================================================
Confidence intervals are an intuitive way to quantify the uncertainty in the observed metric deltas. A 95% confidence interval should contain the true effect 95% of the time: if you ran an experiment 100 times, the true value of the metric delta should fall inside the confidence interval 95 times.
{% figure %}

{% /figure %}
In practical terms, a 95% confidence interval that doesn't contain zero (the green bar above) represents a statistically significant result (with *α = 0.05*). This isn't always the case: there are cases when the p-value of the difference between test and control is statistically significant, but due to uncertainty in the control, a relative delta confidence interval may cross zero (using [The Delta Method](/experiments/statistical-methods/methodologies/delta-method)) or be represented as a point estimate (using [Fieller Intervals](/experiments/statistical-methods/methodologies/fieller-intervals)) while the absolute difference's p-value is statistically significant.
Only 5% of the time would you expect to see the confidence interval exclude zero if the true effect was zero (a.k.a. a false positive). Larger confidence intervals imply less certainty in the exact size of the effect with a larger range of likely values.
## Computing confidence intervals
Statsig calculates confidence intervals using a two-sample z-test. This test requires knowledge of the variance in the metric delta being measured, which Statsig derives differently depending on the type of metric (details [here](/experiments/statistical-methods/variance)).
After establishing the variance of the delta, Statsig computes the confidence intervals.
### Two-sided tests
For the **absolute metric delta**, the confidence interval is given by:
$$
CI(\Delta \overline\{X}) = \Delta \overline\{X} \pm Z\_\{\alpha/2} \cdot \sqrt\{\{var(\Delta \overline\{X})}}
$$
where:
* $Z\_\{\alpha/2}$ is the z-critical value for the desired significance level (1.96 for the standard $\alpha=0.05$ and 95% confidence interval) for a two-sided test
* $var(\Delta \overline\{X})$ is the variance of the absolute delta (details [here](/experiments/statistical-methods/variance))
The confidence interval for the **relative metric delta** can use one of two methods: [Fieller Intervals](/experiments/statistical-methods/methodologies/fieller-intervals) or the [Delta Method](/experiments/statistical-methods/methodologies/delta-method). Customers can choose either method in their Statsig console. Statsig recommends Fieller Intervals and enables them by default for all new customers.
When using Fieller Intervals, the relative metric delta CI can be computed using:
$$
CI(\\% \Delta \overline\{X} ) = \frac\{1}\{1-g} ( \frac\{\overline\{X\_T}}\{\overline\{X\_C}} - 1 \pm \frac\{Z\_\{\alpha/2}}\{\sqrt\{n\_C} \cdot \overline\{X\_C}} \sqrt\{(1-g) \cdot \frac\{var(X\_T)}\{n\_T(n\_T-1)} + \frac\{\overline\{X\_T} var(X\_C)}\{\overline\{X\_C} n\_C (n\_C-1)}})
$$
When using the Delta Method, the confidence interval is:
$$
\begin\{split}
CI(\Delta \overline X\\%)
&= \Delta \overline X\\% \pm Z\_\{\alpha/2} \cdot\sqrt\{\{var(\Delta \overline X\\%)}}\\\\
&= \frac\{\Delta \overline X}\{\overline X\_c} \pm Z\_\{\alpha/2} \cdot\sqrt\{(\frac\{\overline X\_t}\{\overline X\_c})^\{2} \cdot (\frac\{var(X\_c)}\{n\_c \cdot \overline X\_c^2} + \frac\{var(X\_t)}\{n\_t \cdot \overline X\_t^2})} \cdot 100\\%
\end\{split}
$$
If using the Delta Method and the control mean isn't significantly away from zero, then it's simplified to:
$$
\begin\{split}
CI(\Delta \overline X\\%)
&= \Delta \overline X\\% \pm Z\_\{\alpha/2} \cdot\sqrt\{\{var(\Delta \overline X\\%)}} \\\\
&= \frac\{\Delta \overline X}\{\overline X\_c} \pm Z\_\{\alpha/2} \cdot \frac\{\sqrt\{\{var\left(\Delta \overline X\right)}}}\{\overline X\_c} \cdot 100\\%
\end\{split}
$$
### One-sided tests
When running one-sided tests, the form of the confidence interval calculation changes slightly to account for a redistribution of desired false positive rate when looking for increases or decreases in the metric:
$$
CI(\Delta \overline\{X}) = \begin\{cases}
\left\[\Delta \overline\{X} - Z\_\{\alpha} \cdot \sqrt\{\{var(\Delta \overline\{X})}}, \quad +\infty \right) & \text\{if right-hand test}\\\\
\\\\
\left(-\infty, \quad \Delta \overline\{X} + Z\_\{\alpha} \cdot \sqrt\{\{var(\Delta \overline\{X})}} : \right] & \text\{if left-hand test}
\end\{cases}
$$
where:
* $Z\_\{\alpha}$ is the z-critical value for the desired significance level (1.645 for the standard $\alpha=0.05$ and 95% confidence interval) for a one-sided test
* $var(\Delta \overline\{X})$ is the same as for two-sided tests
* the choice of confidence interval depends on if the one-sided test is looking for increases or decreases in the metric
## Welch's t-test for small sample sizes
For small sample sizes, Statsig uses Welch's t-test instead of a standard z-test. Welch's t-test handles samples of unequal size or variance without increasing the false positive rate. The structure of the confidence interval calculation remains the same (depending on whether a 1- or 2-sided test is used), replacing the z-critical value with the t-critical value with degrees of freedom $\nu$.
For a two-sided test, the confidence interval is therefore:
$$
CI(\Delta \overline\{X}) = \Delta \overline\{X} \pm t\_\{\alpha/2} \cdot \sqrt\{\{var(\Delta \overline\{X})}}
$$
$$
\nu = \frac\{\left(var(\overline X\_t) + var(\overline X\_c)\right)^2}\{\frac\{var(\overline X\_t)^2}\{N\_t - 1}+\frac\{var(\overline X\_c)^2}\{N\_c - 1}}\
\= \frac\{var(\Delta\overline\{X})^2}\{\frac\{var(\overline X\_t)^2}\{N\_t - 1}+\frac\{var(\overline X\_c)^2}\{N\_c - 1}}
$$
Where $N\_t$ and $N\_c$ are the number of users in the test and control groups, respectively. For a large number of degrees of freedom, the t-statistic converges with the z-statistic. Therefore, Statsig uses Welch's t-test only when $\nu \< 100$.
## Comparing experiment data to a fixed baseline: one-sample t-test
Sometimes you want to answer questions like "Does my test variant lead to a click-through rate higher than 0.5?" You can define a fixed-baseline comparison when adding metrics to the experiment.
Statsig calculates the confidence interval as
$$
CI(\Delta \overline X) = (\overline X\_\{group} - fixed \ value) \pm Z \cdot\sqrt\{\{var( \overline X\_\{group})}}
$$
================================================================================
# p-Value Calculation
URL: https://docs.statsig.com/statsig-warehouse-native/features/statistics/p-value
Updated: 2026-07-11
================================================================================
In Null Hypothesis Significance Tests, the p-value is the probability of observing an effect at least as large as the measured metric delta, under the assumption that the null hypothesis is true. In practice, a p-value below the pre-defined Type I Error threshold ($\alpha$) serves as evidence of a true effect.
The methodology for p-value calculation depends on the number of degrees of freedom ($\nu$). A two-sample z-test is appropriate for most experiments. Welch's t-test is used for smaller experiments with $\nu \< 100$. In both cases, the p-value depends on the metric [mean](/experiments/statistical-methods/metric-deltas) and [variance](/experiments/statistical-methods/variance) computed for the test and control groups.
Typically, a p-value below threshold $\alpha$ occurs only when the confidence interval does not cross 0. However, this is not always the case in the Statsig UI. When the p-value of the difference between test and control is significant, the relative delta confidence interval may still cross zero (when using [The Delta Method](/experiments/statistical-methods/methodologies/delta-method)) or appear as a point estimate (when using [Fieller Intervals](/experiments/statistical-methods/methodologies/fieller-intervals)), while the absolute difference's p-value remains statistically significant.
## Two-Sample Tests
### Two-Sided z-Test
You can compute the z-statistic (also called the z-score) of a two-sample z-test in multiple equivalent formats:
$$
\begin\{split}
Z &= \frac\{\overline X\_t - \overline X\_c}\{\sqrt\{var(\overline X\_t)+ var(\overline X\_c)}} \\\\
&= \frac\{\overline X\_t - \overline X\_c}\{\sqrt\{var(\Delta \overline\{X})}} \\\\
&= \frac\{\overline X\_t - \overline X\_c}\{\sqrt\{\sigma\_\{\overline\{X}\_t}^2 + \sigma\_\{\overline\{X}\_c}^2}}
\end\{split}
$$
where:
* $Z$ is the observed z-statistic (not the z-critical value $Z\_\{\alpha/s}$)
* $var(\Delta \overline\{X})$ is the variance of the absolute delta of means
* $var(\overline\{X}\_i)$ is the variance of sample means either control or treatment group (details [here](/experiments/statistical-methods/variance))
* $\sigma\_\{\overline\{X}\_t}$ is the standard error of the mean of either control or treatment group (these are the terms you can find in Pulse under the Statistics tab of a metric)
The two-sided p-value is obtained from the standard normal cumulative distribution function:
$$
p-value = 2 \cdot \frac\{1}\{\sqrt\{2\pi}} \int \limits \_\{-\infty}^\{-|Z|}\{e^\{-t^2/2}dt}
$$
### Welch's t-test
For smaller sample sizes, Welch's t-test is preferred because it produces lower false positive rates when group sizes and variances are unequal. In Pulse, Statsig automatically applies Welch's t-test when the degrees of freedom $\nu \< 100$.
The t-statistic (also called the t-score) is computed identically to the two-sample z-statistic above. The degrees of freedom $\nu$ are computed using:
$$
\nu = \frac\{\left(var(\overline X\_t) + var(\overline X\_c)\right)^2}\{\frac\{var(\overline X\_t)^2}\{N\_t - 1}+\frac\{var(\overline X\_c)^2}\{N\_c - 1}}\
:= \frac\{var(\Delta\overline\{X})^2}\{\frac\{var(\overline X\_t)^2}\{N\_t - 1}+\frac\{var(\overline X\_c)^2}\{N\_c - 1}}
$$
The p-value is then obtained from the t-distribution with $\nu$ degrees of freedom.
### One-Sided Z-Test
The one-sided z-test computes the z-statistic $Z$ in the same way as the two-sided test. The one-sided p-value differs as follows:
$$
p-value =
\begin\{cases} 1 - \frac\{1}\{\sqrt\{2\pi}} \int \limits \_\{-\infty}^\{Z}\{e^\{-t^2/2}dt} &\text\{if right-hand test}\\\\
\frac\{1}\{\sqrt\{2\pi}} \int \limits \_\{-\infty}^\{Z}\{e^\{-t^2/2}dt} &\text\{if left-hand test}
\end\{cases}
$$
where:
* $Z$ is computed as in the two-sided test. This uses the signed z-statistic, not the absolute value used in the two-sided p-value.
================================================================================
# Metric Deltas
URL: https://docs.statsig.com/statsig-warehouse-native/features/statistics/metric-deltas
Updated: 2026-07-11
================================================================================
## Computing metric deltas
A metric delta refers to the difference in metric values between two groups, by default the test and control groups. This is the impact measured when evaluating experiment results. To account for the different number of users (or units) in each group, Statsig compares the mean metric value per user, not the total.
{% callout type="note" %}
**Selecting groups**
All deltas are defined as the difference between a "treatment" group and a presumably unchanged "control" group. However, Statsig allows comparison between any two groups.
{% /callout %}
Pulse provides two different metric deltas. The **absolute delta** is the difference between the two means:
$$
\Delta \overline\{X}=\overline\{X}\_t-\overline\{X}\_c
$$
Understanding the impact relative to the baseline value of the metric is often helpful. For example, an absolute delta of +1 clicks/user has different meanings with a baseline value of 1 (+100% increase) vs. a baseline value of 100 (+1% increase). The **relative delta** is computed using the mean of the control group as the baseline:
$$
\Delta \overline\{X} \\%=\frac\{\overline\{X}\_t-\overline\{X}\_c}\{\overline\{X}\_c} \times 100 \\%
$$
{% callout type="note" %}
If you reverse the order of group comparison in Pulse to be "control" vs "treatment", all deltas reverse and the direction of change inverts.
{% /callout %}
## Computing means
Computing group means correctly is critical for obtaining meaningful metric deltas. The methodology for calculating metric means depends on the metric type.
### Event count and sum metrics
These metrics represent totals: number of times an event occurs, sum of time spent, total purchase amount, and similar values. The mean is the average user-level total during the analysis period.
The mean value of the metric $X$ for a group is given by:
$$
\overline\{X}=\frac\{1}\{N} \sum\_\{i=0}^N \sum\_\{d=0}^\{n\_i} X\_\{i, d}
$$
where:
* $N$ is the number of users in the group
* $n\_i$ is the number of days during the analysis period that user $i$ was the experiment
* $X\_\{i,d}$ is the metric value for user $i$ on day $d$
{% callout type="note" %}
Statsig includes only user metrics recorded after a user has been exposed to the experiment in the group mean.
{% /callout %}
### User accounting and Event User metrics (and legacy Event DAU)
Event User metrics set to "Daily Participation Rate" capture the number of distinct users that have the event each day. In Pulse results, these values are normalized by the number of days the user is in the experiment. This represents the probability that a user is daily active for that event (the daily participation rate). The group mean is given by:
$$
\overline\{X}=\frac\{1}\{N} \sum\_\{i=0}^N \frac\{1}\{n\_i} \sum\_\{d=0}^\{n\_i} X\_\{i, d}
$$
where:
* $X\_\{i,d}$ takes value 0 or 1 depending on if user $i$ has the event on a given day $d$.
The following user accounting metrics are computed the same way: *DAU, WAU, MAU\_28day, L7, L14, L28*
For new user accounting (*new\_DAU, new\_WAU, new\_MAU\_28day*), Statsig counts users that are new xAU at some point during the analysis window. The group mean is given by:
$$
\overline\{X}=\frac\{1}\{N} \sum\_\{i=0}^N \max \left(X\_i\right)
$$
Where $\max(X\_i)$ is the maximum value of the new xAU metric for user $i$.
{% callout type="note" %}
**event\_dau** metrics are now in legacy support only and Statsig no longer creates them for new events. Existing event\_dau metrics continue to be available for any of your new experiments and continue to be computed daily. For all new events, create an event\_user metric to measure daily active users.
{% /callout %}
### Custom ratios, means, retention, and stickiness metrics
These metrics include click-through rate, average purchase value, sessions per user, and similar values. Each is obtained by dividing a numerator value, $X$, by a denominator value, $Y$. The mean value of a ratio metric $R$ for an experiment group is given by:
$$
\overline\{R}=\frac\{\frac\{1}\{N} \sum\_\{i=0}^N \sum\_\{d=0}^\{n\_i} X\_\{i, d}}\{\frac\{1}\{N} \sum\_\{i=0}^N \sum\_\{d=0}^\{n\_i} Y\_\{i, d}}=\frac\{\overline\{X}}\{\overline\{Y}}
$$
Where $N$ is the number of users in the experiment group that participate in the metric, i.e. have a non-zero denominator value. $X\_\{i,d}$ and $Y\_\{i,d}$ are the $X$ and $Y$ values for user $i$ on day $d$.
Different approaches exist for ratio metrics in experiments. Statsig selected this implementation because it's statistically sound and interpretable:
* $R$ is the ratio of two means of independent observations: a set of user-level $X$ values and a set of user-level $Y$ values. The central limit theorem can therefore be used to separately obtain the summary statistics of $X$ and $Y$.
* The group means are computed in the same way as the topline metric value, making the means easier to interpret and relate to the topline metric.
### Event User one-time event
For custom **event\_user** metrics with "One-Time Event" selected, Statsig computes how many users have the event at any time after entering the experiment. Statsig doesn't normalize this result by the number of days a user is in the experiment. The group mean is given by:
$$
\overline\{X}=\frac\{1}\{N} \sum\_\{i=0}^N X\_\{i}
$$
where:
* $N$ is the number of users in the group
* $X\_\{i}$ takes value 0 or 1 depending on if user $i$ has the event at any point after entering the experiment
================================================================================
# Standard Error & Mean Variance
URL: https://docs.statsig.com/statsig-warehouse-native/features/statistics/variance
Updated: 2026-07-11
================================================================================
The standard error (sometimes denoted "SE" or "std err") of the mean of each group is required to compute the confidence interval and p-value of a metric delta between those groups. You obtain the standard error of the mean by dividing the sample standard deviation of $X$ by the square root of the number of users in the group.
$$
\sigma\_\{\overline X} = \frac\{\sigma\_\{X}}\{\sqrt\{N}} = \sqrt\{\frac\{var(X)}\{N}} = \sqrt\{var(\overline\{X})}
$$
Standard deviation is the square root of variance. Because variances are easier to manipulate algebraically, Statsig derives the variance for each metric type and then takes the square root to obtain the confidence intervals.
Pulse shows the standard error of the mean of each group alongside the units and mean of each group.
## Computing Variance
The variance of the absolute metric delta is the sum of the variances of the test and control means:
$$
var(\Delta \overline X) =var(\overline X\_t - \overline X\_c) = var(\overline X\_t) + var(\overline X\_c)
$$
Computing this correctly requires accurate variance of the means for each group.
### Count and Sum Metrics
For count and sum metrics, the variance of the sample mean for a given group is obtained directly from the sample variance:
$$
var(\overline\{X}) = \frac\{var(X)}\{N} = \frac\{\frac\{1}\{N-1}\sum\_\{i=0}^\{N}(X\_i-\overline\{X})^2}\{N}
$$
Where:
* $N$ is the number of users in the group
* $X\_i$ is the metric value for user $i$
* $\overline\{X}$ is the user-level average of $X$ for users in that group
### Ratio and Mean Metrics
Ratio and mean metrics combine multiple variables $X$ and $Y$ rather than a single variable $X$. The variance of these metrics depends on both the numerator and denominator variables, which are typically correlated. For metric $R$, the group mean $\overline\{R}$ and group variance of the mean $var(\overline\{R})$ must account for this correlation.
For example, in a *clicks per session* metric, the number of clicks and the number of sessions come from the same group of users and aren't independent.
To account for any correlation, Statsig computes the variance of the mean of a ratio metric $R$ using the delta method:
$$
var(\overline R) = var\left(\frac\{\overline X}\{\overline Y}\right)
:= \left(\frac\{\overline X}\{\overline Y}\right)^2 \cdot \left(\frac\{var(\overline X)}\{\overline X^2} + \frac\{var(\overline Y)}\{\overline Y^2} - 2 \cdot \frac\{covar(\overline X, \overline Y)}\{\overline X\cdot \overline Y} \right)
$$
where the variance of the numerator and denominator means are computed in the same way as detailed above for count metrics, and the covariance is
$$
covar(\overline X, \overline Y) = \frac\{covar(X, Y)}\{N} = \frac\{\frac\{1}\{N-1}\sum\_\{i=0}^\{N}(X\_i-\overline X)\cdot (Y\_i-\overline Y)}\{N}
$$
================================================================================
# Topline and Projected Impact
URL: https://docs.statsig.com/statsig-warehouse-native/features/statistics/topline-impact
Updated: 2026-07-11
================================================================================
The **topline impact** is the average daily effect that an experiment has on the overall metric value, measured between two groups. The **projected launch impact** is an estimate of the daily global metric impact expected if the test group is launched to all users (beyond those in the experiment). This impact is computed relative to the expected baseline metric value without the experiment running.
Statsig shows both Topline Impact and Projected Launch Impact in absolute and relative units. Neither uses CUPED, because CUPED already adjusts for pre-exposure data (the starting point for topline metrics), and combining the two would double-count that adjustment.
{% callout type="tip" %}
**Example**: Take a simple example experiment with a Control group of 1000 users and a Test group of another 1000 users, which ran for 30 days. For an **event\_count** metric, we observed an Experiment Delta of +1.0 events per user (abs). The Topline Impact for this metric would be +33.33 events per day (abs).
{% /callout %}
## Computing Topline Impact
The topline impact is computed over the total duration of the experiment, which produces the most accurate estimate and tight confidence interval. The calculation depends on whether the metric represents an absolute quantity or a ratio:
### Count and Sum Metrics (event\_count, sum)
The absolute topline impact is derived from the experiment results. It depends on the difference in means between test and control and the average number of users in the test group per day.
$$
Impact\_\{abs}=(X\_t-X\_c) \cdot N\_t / n\_\{days}
$$
Using the absolute impact and the overall metric value (as shown in the [metrics dashboard](/metrics/console)), Statsig computes the relative impact: the percentage change in the overall metric value over the rollup window attributed to the active experiment.
$$
Impact\_\{rel}=\frac\{Impact\_\{abs}}\{Topline\_Value-Impact\_\{abs}} \times 100\\%
$$
### Ratio and Mean Metrics
To derive the topline impact on a ratio metric, Statsig first determines the impact on the numerator (*X*) and denominator (*Y*) separately. The topline impact is the current ratio metric value minus the baseline value obtained by subtracting the numerator and denominator impacts:
$$
Impact\_\{abs}=\frac\{Topline\_X}\{Topline\_Y}-Baseline\_Value
$$
Where the baseline value is the expected value of the topline metric if the experiment wasn't running:
$$
Baseline\_Value=\frac\{Topline\_X-(\bar\{X\_t}-\bar\{X\_c}) \cdot N\_t}\{Topline\_Y-(\bar\{Y\_t}-\bar\{Y\_c}) \cdot N\_t}
$$
The relative impact for ratio metrics is obtained by dividing the absolute impact by the baseline value:
$$
Impact\_\{rel}=\frac\{Impact\_\{abs}}\{Baseline\_Value} \times 100\\%
$$
## Computing Projected Launch Impact
The layer allocation of the experiment and the size of the test group are used to estimate a scaling factor *m*, which represents the increase in absolute impact expected when the test group is launched.
The launch factor over a rollup window is calculated as
$$
m\_\{rollup}=\frac\{1}\{\sum\_\{1}^\{rollup}\{layer\_alloc \times group\_pct}} \times rollup
$$
to accommodate changes in allocation during the experiment.
{% callout type="note" %}
The targeting gate isn't factored in. The projected impact calculation assumes that the target gate remains the same after you launch the experiment.
{% /callout %}
### Count and Sum Metrics (event\_count, event\_dau, sum)
For count and sum metrics, the projected absolute impact is the current topline impact scaled by factor *m*. For example, consider an experiment running at 50% layer allocation with a 50/50 test/control split, so that 25% of all users are in the test group. If the allocation changed during the experiment, Statsig uses a weighted average based on historical allocations. If the topline impact is +10 events per day, launching the experiment would produce +40 events per day.
$$
Projected\_\{abs}=Impact\_\{abs} \times m
$$
The relative projected impact is the expected percentage change in the topline metric, relative to the baseline metric value without the experiment running.
$$
Projected\_\{rel}=\frac\{Projected\_\{abs}}\{Topline\_Value-Impact\_\{abs}} \times 100\\% = Impact\_\{rel} \times m
$$
### Ratio and Mean Metrics
The projected impact of ratio metrics depends on the numerator and denominator impacts in the same way as the topline impact calculation. The same scaling factor *m* is used to obtain the projected impact for each term:
$$
Projected\_\{abs}=\frac\{Topline\_X+(m-1) \cdot (\bar\{X\_t}-\bar\{X\_c}) \cdot N\_t}\{Topline\_Y+(m-1) \cdot (\bar\{Y\_t}-\bar\{Y\_c}) \cdot N\_t} - Baseline\_Value
$$
Where the first term represents the projected metric value after launch.
Finally, the projected relative impact of a ratio metric is the projected absolute impact divided by the baseline value of the ratio:
$$
Projected\_\{rel}=(\frac\{Projected\_\{abs}}\{Baseline\_Value}) \times 100\\%
$$
## Confidence intervals
The confidence intervals for topline and projected impact are computed using the same method as the [confidence intervals](/experiments/statistical-methods/confidence-intervals) for experiment deltas.
$$
CI(Impact) = Impact \pm Z \cdot \sqrt\{var(Impact)}
$$
For the absolute impact of count and sum metrics, the variance is a linear combination of the test and control variances:
$$
var(Impact\_\{abs})=\[var(\bar\{X\_t})+var(\{\bar\{X\_c}})] \cdot N\_t^2
$$
And for projected launch impact:
$$
var(Projected\_\{abs})=var(Impact\_\{abs}) \cdot m^2
$$
For ratio metrics and relative impacts, Statsig calculates the variance using the Delta method. This accounts for the correlation between numerator and denominator terms by using Taylor expansion to linearize expressions containing non-linear combinations of experiment variables.
For example, the variance in the relative impact of a count metric is given by:
$$
var(Impact\_\{rel})=var(Impact\_\{abs}) \cdot \frac\{(Topline\_Value - 2 \cdot Impact\_\{abs})^2}\{(Topline\_Value - Impact\_\{abs})^4}
$$
================================================================================
# Variance Reduction
URL: https://docs.statsig.com/statsig-warehouse-native/features/statistics/variance-reduction
Updated: 2026-07-11
================================================================================
## Variance reduction
[Variance](/experiments/statistical-methods/variance) measures the dispersion (or "noise") in a metric or experiment results. Higher variance produces larger confidence intervals, requiring more sample to detect a statistically significant result for the same effect size.
Lower variance reduces required sample size, which leads to shorter experiment run times. Statsig uses a form of CUPED based on a [2013 Microsoft paper](https://www.exp-platform.com/Documents/2013-02-CUPED-ImprovingSensitivityOfControlledExperiments.pdf) (Deng, Xu, Kohavi, & Walker). Statsig automatically applies CUPED to experiments and runs it for the topline results on key metrics in Pulse, producing significant variance reduction for most metrics.
Go to the [CUPED launch post](https://blog.statsig.com/cuped-on-statsig-d57f23122d0e) for more details.
## CUPED - Controlled-experiment Using Pre-Existing Data
CUPED (Controlled-experiment Using Pre-Existing Data) uses user information from before an experiment to reduce variance and increase confidence in experimental metrics. In Statsig, the pre-experiment data window is the 7 days before each user's exposure, rather than a fixed window before the experiment starts. This helps debias experiments where groups were randomly different before any treatment was applied.
The Cloud product uses stratification alongside CUPED to account for users who may not have pre-experiment data. Users are grouped into strata based on available pre-experimentation information. Treatment and control effects are estimated within each stratum, then aggregated to produce an overall result. Standard difference-in-means and variance estimation is then applied. This approach retains users with missing pre-data while still benefiting from variance reduction where applicable.
## Winsorization
Another technique for reducing noise is Winsorization, which manages the influence of outliers. Winsorization measures the percentile *Px* of a metric and sets all values over *Px* to *Px*. This reduces the influence of extreme outliers caused by factors such as logging errors or bad actors.
## Metric selection
The metrics you use can dramatically influence the sensitivity of your analysis. The transformations above, along with techniques like creating threshold-based flags, let you trade exact numbers for significantly more statistical power. Go to the [Statsig blog post on variance reduction](https://www.statsig.com/blog/understanding-and-reducing-variance-and-standard-deviation) for more information.
## Related resources
* [Deng, Xu, Kohavi, & Walker](https://exp-platform.com/Documents/2013-02-CUPED-ImprovingSensitivityOfControlledExperiments.pdf): the seminal paper on using CUPED for online controlled experiments
* [Booking.com on CUPED](https://booking.ai/how-booking-com-increases-the-power-of-online-experiments-with-cuped-995d186fff1d): theory and practice of CUPED
* [Improving the Sensitivity of Online Controlled Experiments: Case Studies at Netflix](https://www.kdd.org/kdd2016/papers/files/adp0945-xieA.pdf)
================================================================================
# Pre-Experiment Bias
URL: https://docs.statsig.com/statsig-warehouse-native/features/statistics/pre-experiment-bias
Updated: 2026-07-11
================================================================================
In some cases, users in two experiment groups have meaningfully different average behaviors before any intervention is applied. If this difference persists after the experiment starts, the analysis may attribute that pre-existing difference to the intervention, making a result appear more or less impactful than it is. [CUPED](/experiments/statistical-methods/methodologies/cuped) helps address this bias, but can't fully account for it.
Some metrics, such as retention, aren't viable candidates for CUPED and can't be easily adjusted.
Statsig measures the pre-experiment values of all scorecard metrics for all experiment groups and determines whether the values are significantly different and could cause misinterpretations. If Statsig detects bias, it notifies users and places a warning on relevant Pulse results.
### How it works
Statsig provides a "Days Since Exposure" view to help identify novelty effects and pre-experiment effects. For example, the test group in the following experiment had a consistently higher mean than the control group in the week before the experiment started:
{% figure %}

{% /figure %}
Statsig detects this bias by running the standard [pulse](/experiments/interpreting-results/read-results) calculation on the pre-experiment term (looking back one week in Cloud, and the configured CUPED lookback window in Warehouse Native), then calculating the p-value for the null hypothesis that the groups are identical. Statsig flags relevant results using logic that balances awareness against false positives from large numbers of scorecard metrics or groups.
### What to Do
Pre-experiment bias can occur by chance and isn't always a major issue.
* If the total delta is small, it may not meaningfully influence the interpretation of results.
* If CUPED can account for the bias, the bias shouldn't affect results.
In many cases, treat this warning as informational and proceed, applying extra scrutiny to impacted metrics. This is appropriate when the metric isn't critical to the experiment or when directional movement matters more than the exact value. Additional experiment time may also reduce the bias if no systemic source exists, because new users dilute the imbalance.
If the metric is critical and the exact numerical value matters, consider resalting and restarting the experiment.
================================================================================
# Bonferroni Correction
URL: https://docs.statsig.com/statsig-warehouse-native/features/statistics/methodologies/bonferroni-correction
Updated: 2026-07-11
================================================================================
## What is Bonferroni correction
A Bonferroni Correction is a statistical method that reduces the probability of false positives by adjusting the significance level for multiple comparisons.
If you run a test with α = 0.05, the probability of a false positive is 5%. Running more comparisons at the same significance level increases the chance of at least one false positive, because each comparison is an additional opportunity for a false positive.
Bonferroni corrections are an optional feature on Statsig experiments that reduce the probability of Type I errors (false positives) by adjusting the significance level (α). Statsig divides the significance level by the number of comparisons being evaluated.
You can choose to apply these based on one or both of the following:
* The number of test groups (multiple treatment hypotheses). Statsig divides the significance level by the number of variants being compared against control.
* The number of metrics in the scorecard. Here you may select what percentage of your total α Statsig divides evenly among the Primary Metrics, and Statsig splits the remaining α equally among Secondary Metrics. For example:
* Significance level of 0.05
* 2 Primary Metrics and 4 Secondary Metrics
* 60% of α applied to Primary Metrics
* Each Primary Metric is calculated with α = 0.6 \* 0.05 / 2 = 0.015
* Each Secondary Metric is calculated with α = 0.4 \* 0.05 / 4 = 0.005
* If both corrections are selected, Statsig applies them on top of each other. In the example above, to also correct for having 2 test groups, further divide each α by 2.
When analyzing dimensions, if correction for metrics is enabled, Statsig applies it separately for the dimensional breakdown. Statsig uses the number of dimensions as the total metric count to correct for *in the dimensional analysis*, but this doesn't impact topline metrics.
{% figure %}

{% /figure %}
## How experiment metrics appear after applying Bonferroni correction
In the experiment scorecard section, Statsig derives confidence intervals from (1 - adjusted α) for applicable metrics. Hovering over a confidence interval displays the adjusted α alongside other relevant metric details.
In the experiment explore section, Statsig calculates a new adjusted α based on your selections, and the confidence intervals use (1 - adjusted α).
================================================================================
# Benjamini–Hochberg
URL: https://docs.statsig.com/statsig-warehouse-native/features/statistics/methodologies/benjamini-hochberg-procedure
Updated: 2026-07-11
================================================================================
## What the Benjamini-Hochberg procedure is
The Benjamini-Hochberg Procedure ("BH" procedure) is a statistical method that reduces the probability of false positives by adjusting the significance level for multiple comparisons. It isn't as extreme as a [Bonferroni Correction](/experiments/statistical-methods/methodologies/bonferroni-correction), because instead of controlling the chance of at least one false positive (Family Wise Error Rate), BH controls the expected value of false positives when the null hypothesis has been rejected (False Discovery Rate).
You can enable the BH procedure for individual experiments, or configure global Experiment Settings to enable it by default.
{% figure %}

{% /figure %}
## Methodology
The [Benjamini-Hochberg Procedure](https://www.statisticshowto.com/benjamini-hochberg-procedure/) updates the significance level (modifying your pre-set $\alpha$). Statsig calculates the new significance level by sorting the p-values of metrics in ascending order and comparing each with a paired threshold. Each p-value’s paired threshold is the desired False Discovery Rate divided by the number of comparisons being evaluated, multiplied by the rank of that p-value in the ordered list. The largest threshold value that is higher than its corresponding p-value becomes the new significance level ($\alpha$).
You can apply the Benjamini-Hochberg Procedure based on:
* The number of test groups (multiple treatment hypotheses). For each metric aggregate the list of p-values from each variant and complete the Benjamini-Hochberg procedure.
* The number of metrics in the scorecard. For each variant aggregate the list of p-values from each metric and complete the Benjamini-Hochberg procedure.
* Both the number of test groups and number of metrics in the scorecard. Statsig aggregates all p-values to complete the Benjamini-Hochberg procedure.
Statsig doesn't apply the BH procedure when evaluating the p-values of any event-dimension or user-property experiment metric results. Only the top-line metric results are compared to the new significance level.
## How experiment metrics appear after applying Benjamini-Hochberg
In the experiment scorecard section, Statsig derives confidence intervals from (1 - adjusted α) for applicable metrics. Hovering over a confidence interval displays the adjusted α alongside other relevant metric details.
In the experiment explore section, Statsig calculates a new adjusted α based on your selections, and the confidence intervals use (1 - adjusted α).
================================================================================
# CUPED
URL: https://docs.statsig.com/statsig-warehouse-native/features/statistics/methodologies/cuped
Updated: 2026-07-11
================================================================================
## CUPED: Controlled-experiment using pre-existing data
CUPED (Controlled-experiment Using Pre-Existing Data) is a technique that uses user information from before an experiment to reduce variance and increase confidence in experimental metrics. At Statsig, this pre-experiment data is defined as the 7 days before each user's exposure, rather than a fixed window before the experiment starts for all users. This helps to debias experiments that have meaningful pre-exposure bias (for example, groups that were randomly different before any treatment was applied).
The Cloud product uses a 7-day window for CUPED calculation. For Warehouse Native customers, a 7-day window is recommended, but you can customize it to any length.
For more information, go to the [Variance Reduction](/experiments/statistical-methods/variance-reduction) page.
For an in-depth look at the methodology, refer to CURE by Statsig.
## CUPED for simple aggregations
The methodology for simple aggregations is described in the original [Microsoft paper](https://www.exp-platform.com/Documents/2013-02-CUPED-ImprovingSensitivityOfControlledExperiments.pdf) and in Statsig's in-depth [article](https://www.statsig.com/blog/cuped) on the technique.
The Cloud product uses stratification alongside CUPED to account for users who may not have pre-experiment data. Users are grouped into strata based on available pre-experimentation information. Treatment and control effects are first estimated within each stratum, then aggregated to produce an overall result. The standard difference-in-means and variance estimation is then applied. This approach retains users with missing pre-data while still benefiting from variance reduction where applicable.
## CUPED for ratio metrics
The Microsoft paper also gives details on how to implement CUPED for a different analysis unit (Appendix B). Statsig extends this to work for ratio metrics, where each experiment unit has a numerator and a denominator. The variance reduction process works by finding the variance of experiment data, pre-experiment data, and the covariance between the two.
Denote the numerator, denominator, pre-experiment numerator, and pre-experiment denominator of a unit as $Y$, $N$, $X$, and $M$, respectively. Using the CUPED-reduced variance formula,
$$
Var(\frac\{Y\_\{cv}}\{N\_\{cv}})=Var(\frac\{Y}\{N})+\theta^2 Var(\frac\{X}\{M})-2\theta Cov(\frac\{Y}\{N}, \frac\{X}\{M})
$$
where optimal $\theta$ is found as
$$
\frac\{Cov(\frac\{Y}\{N}, \frac\{X}\{M})}\{Var(\frac\{X}\{M})}
$$
expanded to
\\
$$
\frac\{Cov(\frac\{Y}\{\mu\_N}-\frac\{\mu\_Y N}\{\mu^2\_N}, \frac\{X}\{\mu\_M}-\frac\{\mu\_X M}\{\mu^2\_M})}\{Var(\frac\{X}\{\mu\_M}-\frac\{\mu\_X M}\{\mu^2\_M})}
$$
From this:
$$
\frac\{\hat\{Y\_\{c}}}\{\hat\{N\_\{c}}}=\frac\{Y\_\{c}}\{N\_\{c}}-\theta( \frac\{X\_\{c}}\{M\_\{c}} - \mathbb\{E}\[R])
$$
$$
\frac\{\hat\{Y\_\{t}}}\{\hat\{N\_\{t}}}=\frac\{Y\_\{t}}\{N\_\{t}}-\theta( \frac\{X\_\{t}}\{M\_\{t}} - \mathbb\{E}\[R])
$$
Because $\mathbb\{E}\[R]$ is difficult to derive and the expectation term is the same for both groups, Statsig substitutes $\mathbb\{E}\[R]$ with $\frac\{X\_\{c}}\{M\_\{c}}$, transforming the formulas above to:
$$
\frac\{Y\_\{cv}(control)}\{N\_\{cv}(control)}=\frac\{Y(control)}\{N(control)}
$$
$$
\frac\{Y\_\{cv}(test)}\{N\_\{cv}(test)} \\\\
:=\frac\{Y(control)}\{N(control)} - (\frac\{Y(control)}\{N(control)} - \theta \frac\{X(control)}\{M(control)}) + (\frac\{Y(test)}\{N(test)} - \theta\frac\{X(test)}\{M(test)}) \\\\
:=\frac\{Y(test)}\{N(test)} - \theta\frac\{X(test)}\{M(test)} + \theta \frac\{X(control)}\{M(control)}
$$
Using the optimal $\theta$, Statsig reduces group-level variance by plugging the parameter back in to calculate the adjustment. Across-group $\theta$ doesn't necessarily reduce variance for one group, or the sum of variances of all groups, but in most cases it does. Simulation shows that 98.3% of metrics saw a decrease with CUPED.
Statsig uses CUPED variance when all of the following conditions hold:
* Core assumptions of the CUPED model are satisfied; this can be violated due to rounding error or other data artifacts
* E(X\_hat) = E(X)
* The pooled variance of the adjusted population across groups is \< the variance of the unadjusted population
* Enough units have pre-experiment values (> 100)
* Enough percentage of units have pre-experiment values (> 5%)
================================================================================
# Delta Method
URL: https://docs.statsig.com/statsig-warehouse-native/features/statistics/methodologies/delta-method
Updated: 2026-07-11
================================================================================
## Delta method for ratio metrics
Statsig uses the delta method when calculating the variance for variables that have a numerator and denominator.
The variance of ratio and mean metrics depends on the numerator and denominator variables, which are typically correlated. For example, consider a *clicks per session* metric. The number of clicks and the number of sessions are two sets of observations coming from the same group of users, so they aren't independent of each other. To properly account for this correlation, Statsig obtains the variance of a ratio metric *R* using the delta method:
{% figure %}

{% /figure %}
where the variance of the numerator and denominator means are computed in the same way as detailed above for count metrics, and the covariance is
{% figure %}

{% /figure %}
## Delta method for relative lifts
Statsig may also use the delta method when calculating the confidence interval for relative lifts. The other methodology for calculating confidence intervals for relative lifts is [Fieller Intervals](/experiments/statistical-methods/methodologies/fieller-intervals). The delta method is a heuristic for Fieller Intervals that converges with a large population.
================================================================================
# Fieller Intervals
URL: https://docs.statsig.com/statsig-warehouse-native/features/statistics/methodologies/fieller-intervals
Updated: 2026-07-11
================================================================================
## Fieller intervals
You can use Fieller Intervals as the methodology for calculating confidence intervals for the relative change between test and control group.
The Delta Method is an approximation for the variance of a ratio between two variables that is then used to establish a confidence interval. Fieller Intervals are an exact solution for the confidence interval.
In most cases, Fieller Interval results are very similar to results from the Delta Method. Because Fieller Intervals are more accurate, Statsig recommends using this methodology.
## Calculation
### 1: Determine if a Fieller interval is well-defined
Before applying Fieller’s Theorem, Statsig checks that the denominator of the relative lift metric $\overline\{X\_C}$ is significantly distinct from 0.
Statsig calculates the parameter $g$:
$$
g = \frac\{Z\_\{\alpha/2}^2 \cdot \mathrm\{var}(X\_C)}\{(n\_C-1) \cdot \overline\{X\_C}^2}
$$
Where:
$Z\_\{\alpha/2}$ is the critical value associated with the desired confidence level $\mathrm\{var}(X\_C)$ is the variance of the control group metric values $n\_C$ is the number of units in the control group $\overline\{X\_C}$ is the mean of the control group metric values
When $g$ \< 1, the control mean is significantly different from 0, and Fieller intervals apply.
### 2A: Apply Fieller interval formula
Since the control and test group results are independent of each other, covariance terms in Fieller's Theorem can be dropped.
$$
CI(\\% \Delta \overline\{X} ) = \frac\{1}\{1-g} \left( \frac\{\overline\{X\_T}}\{\overline\{X\_C}} \pm \frac\{Z\_\{\alpha/2}}\{\overline\{X\_C}} \sqrt\{ \frac\{\overline\{X\_T}^2}\{\overline\{X\_C}^2} \cdot \frac\{\mathrm\{var}(X\_C)}\{n\_C-1} + (1-g)\frac\{\mathrm\{var}(X\_T)}\{n\_T-1} } \right) - 1
$$
### 2B: Edge case: control mean not statistically distinct from zero
In rare cases (less than 5% of observed metric comparisons on Statsig), g $\geq$ 1, which means the control group’s mean isn’t statistically distinguishable from 0.
When $\overline\{X\_C}$ isn't statistically different from zero, the denominator of the relative lift calculation is unstable. This means that the confidence interval for the percent difference between test and control is unbounded.
When this happens, Statsig surfaces the relative lift observed during the experiment.
$$
\\% \Delta \overline\{X} = \frac\{\overline\{X\_T}-\overline\{X\_C}}\{\overline\{X\_C}}
$$
## Enabling Fieller intervals in Statsig
Configure the relative confidence interval methodology in *Experimentation Settings* at the organization level. Changing this setting only affects experiments created after the setting change.
{% figure %}

{% /figure %}
In many cases, the results are effectively the same as using the [Delta Method](/experiments/statistical-methods/methodologies/delta-method). However, if you are running experiments with small sample sizes or noisy denominators, Fieller Intervals are more reliable, and Statsig strongly recommends using them.
In the experiment scorecard, Fieller Intervals appear as shown below.
{% figure %}

{% /figure %}
================================================================================
# SRM Checks
URL: https://docs.statsig.com/statsig-warehouse-native/features/statistics/methodologies/srm-checks
Updated: 2026-07-11
================================================================================
## How SRM checks work
Sample ratio mismatch (SRM) occurs when the observed allocation of **unique** users between test groups differs from the expected allocation or "split" of the test. Go to the [Statsig blog post on SRM](https://www.statsig.com/blog/sample-ratio-mismatch) for background.
SRM is a signal that there could be unknown bias in the test. Unless you can clearly diagnose the reason for the imbalance, there's no reliable way to know how much this bias affects your results.
Statsig runs SRM checks on all experiments and feature gates as part of Health Checks (described in [Health Checks](/experiments/monitor)). Statsig uses a Chi-squared test to identify whether the split of users between groups indicates a sample ratio mismatch.
{% figure %}

{% /figure %}
Statsig automatically analyzes data by common dimensions logged by the Statsig SDK to identify potential drivers of SRM. These include sdk\_type, sdk\_version, reason, is\_bot, browser\_name, browser\_version, os, os\_version, and region.
{% figure %}

{% /figure %}
================================================================================
# Winsorization in Warehouse Native
URL: https://docs.statsig.com/statsig-warehouse-native/features/statistics/methodologies/winsorization
Updated: 2026-07-11
================================================================================
Statsig Warehouse Native lets you configure winsorization per metric to cap extreme values and reduce outlier noise in experiment results. Winsorization measures the percentile Px of a metric and sets all values over Px to Px.
Statsig computes the Px value using all non-zero and non-null unit-level values of the metric; metrics are aggregated from rows or events, and then the Px'th unit's value is used as the threshold to adjust other units' values.
{% tabs %}
{% tab title="Statsig Cloud" %}
At Statsig, the default percentile for winsorization is 99.9%. This reduces the influence of extreme outliers caused by factors such as logging errors or bad actors.
Statsig applies winsorization to sum, event count, mean, ratio, and funnel metrics, including imported metrics. Statsig doesn't apply winsorization to Participation or User Accounting metrics.
{% /tab %}
{% tab title="Warehouse Native" %}
Statsig Warehouse Native lets you configure winsorization per metric and choose the upper and lower bounds to apply.
{% figure %}

{% /figure %}
Statsig applies winsorization to sum, event count, mean, and ratio metrics.
{% /tab %}
{% /tabs %}
## Metric capping
Metric capping is an effective technique for handling outliers. You can define maximum values for a metric for any unit type configured for that metric. Statsig automatically adjusts any value exceeding the cap downward to the cap value.
For example, if purchases greater than $10,000 per day shouldn't skew analysis, Statsig adjusts any transaction exceeding that threshold downward to the limit. Metric capping supports Event Count and Aggregation (sum) metric types.
================================================================================
# One-Sided Tests
URL: https://docs.statsig.com/statsig-warehouse-native/features/statistics/methodologies/one-sided-test
Updated: 2026-07-11
================================================================================
## One-sided tests (also known as one-tailed tests and non-inferiority tests)
A one-sided test lets you test for a metric moving in only one direction, which you specify in advance. This trade-off gives you additional sensitivity (or power). It differs from the standard Pulse results, which show two-sided results by default.
Use cases for one-sided testing include detecting regressions in guardrail metrics and testing for a change where only one direction has meaningful business impact. For example, you may be less interested in detecting whether a new feature reduces crash rates, but very interested in learning whether the new feature increases crash rates. In that case, you are willing to forgo detecting the reduction in favor of better detecting the increase.
{% callout type="info" %}
One-sided tests completely disregard the possibility of detecting the metric moving in the direction that isn't specified, but they give you higher sensitivity in the direction you are looking (which allots all your alpha to testing statistical significance in the one direction of interest). This results in one-sided confidence intervals (CIs) that are narrower in the direction of interest than their two-sided counterparts.
{% /callout %}
## How to enable one-sided tests
When setting up an experiment and identifying metrics to measure, the default setting is to run a two-sided test. To change this, click the metric name on the experiment setup screen. A popup opens where you can modify the test type and indicate the direction you want to measure.
{% callout type="note" %}
V1 doesn't support Bayesian testing yet.
{% /callout %}
{% figure %}

{% /figure %}
## How to read one-sided test results
Metrics using one-sided tests appear in Pulse very similarly to two-sided tests. The only difference is that Statsig shows a one-sided CI rather than a two-sided CI.
One-sided CIs can be confusing at first. They extend to infinity or negative infinity, which is expected because Statsig only detects changes in the specified direction. As with all CIs, they indicate that the real mean value of the metric likely falls within this range. Because the CI for a one-sided metric is wide, it is equally useful to read the results as indicating high confidence that the mean value doesn't fall outside the CI.
{% figure %}

{% /figure %}
## FAQ
#### Why can't I just run two one-sided tests
Running two one-sided tests would result in a less powerful test. One-sided tests work by allocating the entirety of Type I error (alpha/significance) to one direction. Adding a one-sided test in the other direction reintroduces the chance of making a Type I error in that direction. The result is confidence intervals that appear tighter but actually produce higher rates of decision error than the specified confidence level (default: 95%).
#### Why use a one-sided test rather than a two-sided test
The choice depends on your use case, metric of interest, and the business impact of any decision. Choose one-sided tests when you plan to act only on changes in one direction and detecting changes in the other direction has no business value.
================================================================================
# CURE
URL: https://docs.statsig.com/statsig-warehouse-native/cure/introduction
Updated: 2026-07-11
================================================================================
CURE (variance Control Using Regression Estimates) is Statsig's extended implementation of [CUPED](/experiments/statistical-methods/methodologies/cuped), a technique that uses pre-experiment data for each experimental unit to control variance observed in an online experiment. CUPED can reduce variance moderately to significantly and can correct for some pre-existing differences between experiment groups.
CUPED provides additional statistical power without requiring additional users or experiment runtime. However, CUPED has one major limitation: it relies on pre-experiment data for a given metric to reduce that metric's variance. This means that:
* New user experiments can't leverage the technique
* Metrics which aren't autocorrelated don't see significant variance reduction
## What is CUPED
Refer to the [CUPED documentation](/experiments/statistical-methods/methodologies/cuped) or the [CUPED blog post](https://www.statsig.com/blog/cuped).
## How CURE improves on CUPED
CURE solves the no-covariate problem by allowing practitioners to specify additional covariates, which are used in the CUPED regression. The predictions from this regression are used to generate an estimator set that is unbiased, but has lower variance than the original estimator.
CURE can reduce experiment runtimes more than CUPED alone. When combined with Statsig's Entity Properties, CURE allows practitioners to connect existing feature stores to this variance reduction technique.
{% callout type="note" %}
Like CUPED, CURE can modify the point estimates of groups, though the total value across all experimental groups sums to the same value as in the unadjusted dataset.
Drift in value between groups is expected, particularly when there are pre-experiment differences and the correlation is high. This is the algorithm adjusting for pre-existing deltas in experiment groups. Contact the team in Slack if you have concerns.
{% /callout %}
## How CURE differs from standard regression
This technique is similar to standard regression estimates used in other experimentation tools, but differs in several ways:
* Statsig applies CURE across all major experimental surfaces, including experiment scorecards, drill-down explore queries, filtered queries, and power analysis. This ensures results match between scorecards and explore queries, which can diverge in tools with incomplete CUPED implementations.
* Multivariate regressions enable variance reduction for new users or users without pre-experiment data.
* CURE manages feature selection, preventing overfitting issues seen in similar multivariate approaches, and making regression adjustments more transparent.
## How CURE calculates variance reduction
The original CUPED implementation by Microsoft [(Deng, et. al.)](https://www.exp-platform.com/Documents/2013-02-CUPED-ImprovingSensitivityOfControlledExperiments.pdf) is equivalent to an OLS regression with one independent variable. However, the adjustment doesn't need to be a single-variable regression. A regression guarantees identical or lower variance, but any unbiased adjustment is mathematically valid.
By calculating the covariance matrix between covariates and experiment outcomes in SQL, you can generate the inputs for multivariate regressions and use these to adjust post-exposure values in an unbiased manner, meaning the regression is demeaned and uses only pre-experiment data. This yields an estimator with variance less than or equal to the original estimator. Specifically:
* if covariates don't exist, nothing happens and the adjusted estimator is identical to the unadjusted
* in the worst case where covariates are uncorrelated, the adjusted estimator is identical to the unadjusted
* if no additional covariates are applied, CURE is mathematically equivalent to CUPED
* if additional covariates are applied that increase the $R^2$ (i.e. portion of explained variance), CURE reduces variance more than CUPED
## CURE for ratio metrics
Combining Statsig's CUPED implementation for ratio metrics with CURE covariates enables ratio metrics to use both pre-experimental data and user-provided covariates. With an adjusted formula to calculate the covariance of a ratio random variable to a simple variable, Statsig establishes a multi-covariate regression similar to CURE for simple metrics. The variance and mean adjustments follow the ratio CUPED approach.
* As an artifact of the regression, Statsig obtains the shrinkage $R^2$, which it uses to scale the post-adjustment variance.
* Statsig constructs the mean adjustment as the inner product of the coefficient vector $\theta$ and the covariate group means $C$. By calculating the difference in adjustment between the test and control group, only the test group mean is adjusted while holding the control group mean the same.
## Data sources
When CUPED is enabled, pre-experiment metric data is still an input to the CURE regression.
CURE allows you to pull data from:
* Assignment records: if using end-to-end experiments with Statsig, log user attributes using the SDK and they are exported for use in CURE.
* Entity property sources: use any timestamp-enabled entity property source to provide covariate data. Non-timestamped entity property sources are disabled to prevent data leakage into CURE.
Entity property sources are a convenience feature; you could alternatively manage joining user features to your assignment data. In practice, this enables use cases such as providing your own table of regression estimates as covariates, similar to DoorDash's [CUPAC](https://careersatdoordash.com/blog/improving-experimental-power-through-control-using-predictions-as-covariate-cupac/) approach.
## Configuration
You can manage CURE in two places:
* Project Settings under Experimentation: the default CURE Covariates setting lets you specify covariates to use across any CUPED-enabled metric in your project.
* Advanced Settings in the Experiment Settings page: specify covariates for a single experiment.
## Datasets
Statsig surfaces CURE information in experiment diagnostics, providing details on coefficients and relevant covariates for any given metric. View the adjustments used under the Regression Adjustment job, and check the coefficients table to run your own analysis on the regression model.
## Outputs
View the DAG history to see the exact coefficients used and the table where they are stored. Statsig also renders an in-console explanation of which variables contributed to variance reduction and by how much, which helps users decide which features to use.
## Known limitations
Like CUPED, CURE can modify the point estimates of groups. The total value across all experimental groups sums to the same value as in the unadjusted dataset, but drift in value between groups is expected, particularly when there are pre-experiment differences and the correlation is high. This is the algorithm adjusting for pre-existing deltas in experiment groups. Contact the team in Slack if you observe a concerning change.
## Feature selection
Statsig uses Lasso regression to select important features, reducing the computational cost of adjusting the unit metric level and controlling for multicollinearity. View the contribution of each feature on the variance reduction card. The estimated proportion of variance reduction is:
$$
\text\{Variance Reduction}\_\{i} = \frac\{\{Coefficient\_\{i}}^2\*Var(feature\_\{i})}\{\sum \{Coefficient\_\{i}}^2\*Var(feature\_\{i})}
$$
by that,
$$
\sum \text\{Variance Reduction}\_\{i} = 100\\%
$$
## References
* [From Augmentation to Decomposition: A New Look at CUPED in 2023](https://arxiv.org/html/2312.02935v1)
* [Improving Experimental Power through Control Using Predictions as Covariate (CUPAC)](https://careersatdoordash.com/blog/improving-experimental-power-through-control-using-predictions-as-covariate-cupac/)
* [Agnostic notes on regression adjustments to experimental data: Reexamining Freedman’s critique](https://projecteuclid.org/journals/annals-of-applied-statistics/volume-7/issue-1/Agnostic-notes-on-regression-adjustments-to-experimental-data--Reexamining/10.1214/12-AOAS583.full)
================================================================================
# Get Started With CURE
URL: https://docs.statsig.com/statsig-warehouse-native/cure/cure-setup
Updated: 2026-07-11
================================================================================
## Defaults
By default, CURE runs a univariate regression when enabled, which is the same as the [standard implementation of CUPED](https://www.exp-platform.com/Documents/2013-02-CUPED-ImprovingSensitivityOfControlledExperiments.pdf).
To add additional covariates
## Project defaults
In project settings under Experimentation, specify default covariates for your company:
{% figure %}

{% /figure %}
You can source covariates from the assignment source for the experiment, or from Entity Property Sources.
Properties with an Entity Property Source name attached come from a property source and Statsig always includes them on the experiment. Properties without an EPS come from an assignment source and apply if the column exists on the assignment source of a given experiment.
## Experiment settings
Per-experiment, specify additional covariates or remove covariates specific to your analysis:
{% figure %}

{% /figure %}
Statsig populates this list from the experiment assignment source columns and relevant Entity Property Sources.
## Metric:Metric
One tool this framework enables is metric:metric covariates, such as using units' pre-experiment clicks as a covariate for units' in-experiment revenue. Statsig doesn't use this approach for several reasons:
* Consistency: adding a new metric to your analysis should not significantly alter the results of other metrics.
* Unnecessary complexity: if you have key metrics that function as covariates, explicitly provide these for all metrics as a covariate in an entity property source. This achieves the same result without opaque outputs from unknown metric covariances.
Statsig recommends explicitly including key metric covariates as numerical covariates rather than adding arbitrary covariates based on metric selection. Contact the team in Slack to discuss further.
## Preventing adjustments
You can turn off CUPED in your Pulse results, and can create a project-level setting to enforce this. CURE still runs when CUPED is turned off, which incurs some compute cost. To avoid running CURE, turn it off on a given metric by clearing the CUPED option in the metric's setup page.
{% figure %}

{% /figure %}
================================================================================
# Autotune
URL: https://docs.statsig.com/statsig-warehouse-native/features/autotune
Updated: 2026-07-11
================================================================================
Autotune Experiments in Warehouse Native have a setup similar to cloud [Autotunes](/autotune/overview). In Warehouse Native, Autotune success events are pulled from a Metric Source, and results are computed in your warehouse.
{% callout type="info" %}
Contact the team to get Continuous Autotune enabled in your account.
{% /callout %}
## Creating an Autotune
To set up an Autotune on Warehouse Native, start by defining your objective. The objective can be an *event* or a *value* from a Metric Source. For detailed instructions, refer to [how to set up a Metric Source](/statsig-warehouse-native/configuration/metric-sources).
You can also specify the optimization direction (Maximize or Minimize) to determine how Autotune evaluates variant performance.
{% figure %}

{% /figure %}
1. Navigate to the [Experiments section](https://console.statsig.com/experiments) in the sidebar of the Statsig Console.
2. Click on the [Autotune tab](https://console.statsig.com/autotune) at the top.
3. Click the Create button and enter the name and description of the Autotune Experiment that you want to create.
4. Select an ID Type for your Experiment.
5. Create and name your variants for your Autotune Experiment. The variant listed as Control/Default is returned when the Autotune Experiment isn't running.
6. Select your Metric Source that you defined earlier as shown below.
{% figure %}

{% /figure %}
The following parameters are available:
| Parameter | Description |
|---|---|
| Exploration Window | The initial time period where Autotune splits traffic equally. Useful for noisy or temporal metrics where hourly swings can bias initial measurements. |
| Attribution Window | The maximum duration between the exposure and success event that counts as a success. 1 hour is recommended for most applications; adjust if the success event is expected to lag the exposure event by several hours. |
| Winner Threshold | The "probability of best" threshold a variant must reach for Autotune to declare it the winner, stop collecting data, and direct all traffic. A lower value results in faster decisions but increases the probability of selecting the wrong winner. |
Click **Create** to finalize the setup.
7. The Autotune is configured and ready. Click **Start** when you are ready to launch your Autotune test.
================================================================================
# Meta-Analysis
URL: https://docs.statsig.com/statsig-warehouse-native/features/meta-analysis
Updated: 2026-07-11
================================================================================
## How meta-analysis works
As teams run many experiments, it becomes possible to identify patterns across those experiments. This is meta-analysis. Common questions that meta-analysis helps answer include:
- How hard is a metric to move?
- Are there more sensitive proxies for the metric you care about?
- How are teams performing relative to each other?
Statsig has worked with multiple organizations to help them run thousands of trustworthy experiments per year. These tools are useful whether you are running 50 experiments a year or 5000. To influence the roadmap, reach out in [Slack](https://statsig.com/slack).
## Experiment timeline view
This view lets you filter to experiments a team has run. At a glance you can answer questions like:
1. What experiments are running now?
2. When are they expected to end?
3. What % of experiments ship Control vs Test?
4. What is the typical duration?
5. Do experiments run for their planned duration - or much longer or shorter?
6. Do experiments impact key business metrics - or only shallow or team level metrics?
7. How much do they impact key business metrics?
{% figure %}

{% /figure %}
## Metric impact (batting average)
The "batting average" view shows how easy or hard a metric is to move. You can filter to a set of shipped experiments and see how many moved a metric by 1% vs 10%. As with other meta-analysis views, you can filter by team, tag, or statistical significance.
Common uses include:
- Validating whether a claim that the next experiment will move a metric by 15% is reasonable.
- Establishing realistic goals based on past ability to move the metric.
{% figure %}

{% /figure %}
## Metric correlation view
This view plots two metrics on the same chart, where each data point represents one experiment's impact. Use it to assess whether the metrics tend to move together.
Often the metric you want to move isn't very sensitive and takes a while to measure. Finding more sensitive, faster-to-measure proxy metrics is useful for running experiments. You can also remove outliers, filter to a team's experiments, or download the underlying dataset.
In this example: "Checkouts" is the target metric, but it isn't very sensitive. "AddToCart" correlates well with "Checkouts", while "ViewItemDetail" doesn't.
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
## Metric insights
This view lets you pick a metric and see all experiments and feature rollouts that impact this metric. [Learn more](/experiments/exploring-results/aggregated-impact).
{% figure %}
{% /figure %}
## Knowledge bank
The Knowledge Bank is a searchable repository of experiment learning across teams. Use it to find shipped, healthy experiments, gain context on past work, and generate ideas for new experiments.
It gives new team members a way to explore experiments by keyword when the structured meta-analysis views don't fit the need. The meta-analysis tools provide more structured discovery, but free text search is also available here.
{% figure %}

{% /figure %}
================================================================================
# Practical Use Cases
URL: https://docs.statsig.com/statsig-warehouse-native/features/use-case
Updated: 2026-07-11
================================================================================
## How can I define and filter results to 'new users' only?
### Scenario
I want to filter the experiment results to new users only: A user is 'new' if they had never visited the website before they were exposed to this experiment. If the first time the user visited the website was after the exposure timestamp in this experiment, the user was a 'new user'; otherwise the user was an existing user.
### Solution
Assuming you have a logging table that contains every visit with a relative timestamp (`logging`), and another table with all users' `user_id` values (`user`), first create an Entity Property with the following logic:
```sql expandable
select distinct
user_id,
'new' as new_user,
timestamp('1900-01-01') as timestamp
from user -- you can add filters here to include only targeted population only
union all
select distinct
user_id,
'existing' as new_user,
timestamp as timestamp
from logging
```
Select **Run Query** to return a table like this. Select **Save Results** to use this Entity Property across all experiments and gates to filter or group experiment results by whether a user is new or existing.
| user\_id | new\_user | timestamp |
| ------------- | -------------- | ------------------------- |
| A | new | 1900-01-01T00:00:00+00:00 |
| A | existing | 2024-01-01T10:10:18+00:00 |
| B | new | 1900-01-01T00:00:00+00:00 |
***
## How can I analyze logged-in metrics when my experiment exposures are at logged-out grain?
### Scenario
I want to run an experiment to find out which version of my website design leads to a higher signup rate among new visitors. The experiment assignment will occur when a logged-out user visits my website, and they will be exposed to one of the design variants.
When a user decides to sign up, a new and unique logged-in user ID will be generated for them. To calculate the conversion rate (CVR) accurately and consistently, I need a reliable way to map the logged-out IDs to the corresponding logged-in IDs. This will allow me to attribute each signup to the correct experiment variant and evaluate which design performs better in driving conversions.
### Solution
Statsig supports two commonly used approaches for this mapping, each suited to different business scenarios:
**Strict 1:1 Mapping:** Keeps only records where there is a unique, unambiguous mapping between the logged-out ID and the logged-in ID. Statsig discards any records with duplication (for example, multiple logged-out IDs mapping to the same logged-in ID or the reverse). Use this approach when accuracy and clarity are the top priorities and data duplication is rare.
**First Touch Mapping:** When a logged-in ID maps to multiple logged-out IDs, retains only the first association and discards the rest. Better suited for scenarios where you want to preserve as much data as possible and duplications are common (for example, users frequently access the website from multiple devices or sessions).
For either mapping approach, you need to create a mapping between the logged-out ID and the logged-in ID by setting up an Entity Property with both IDs present or creating an exposure assignment source with columns for both ID types.
#### Step 1: Advanced settings for your experiments
When setting up your experiment, navigate to *Setup > Advanced Settings*, and select your Secondary ID type (the log-in user ID in this scenario).
{% figure %}

{% /figure %}
#### Step 2: Identify the mapping mode that suits your need
{% figure %}

{% /figure %}
#### Step 3: Choose your Entity Property Source
{% figure %}
{% /figure %}
* **\[Recommended]** Create a new Entity Property Source for your ID Resolution mapping if you haven't already.
Go to *Data > Entity Properties*. You can either enter an existing table with the ID mappings or write a new query to create the mappings with the following logic:
```sql
SELECT stable_id, user_id, timestamp
FROM id_mapping
```
* You can also choose **"None"** to use your assignment source for the mapping.
Create your new assignment source by going to *Data > Assignment Sources*. Statsig doesn't recommend this option because it becomes more complex to manage as your experiments scale.
================================================================================
# MEX on Warehouse Native
URL: https://docs.statsig.com/statsig-warehouse-native/features/mex-on-warehouse-native
Updated: 2026-07-11
================================================================================
[Metrics Explorer](/product-analytics/overview) (or MEX) is Statsig's analytics solution. With Warehouse Native, MEX integrates directly on top of your tables to provide analysis on the same datasets you use for experimentation.
{% callout type="info" %}
Contact the team to get Metrics Explorer enabled in your account.
{% /callout %}
## Functionality
### [Drill-Down and Filter](/product-analytics/overview)
Filter and group by fields to calculate measures as timeseries, bar-charts, and more.
### [Funnel Analysis](/product-analytics/funnels)
Run funnel analysis to understand how users move through your product.
### [Group By Experiment](/product-analytics/drilldown#drilling-down)
View arbitrary timeseries and rollups grouped by experiment membership, allowing more observational analysis of what happened during your experiment.
### [Retention Chart](/product-analytics/drilldown#retention)
Analyze user retention reports to understand how effectively your product maintains user engagement over time.
### [Distribution Chart](/product-analytics/drilldown#distribution)
View distribution charts to help you visualize the range of user experiences across your product.
### Save, share, and view SQL
Save analyses and share them with coworkers. Viewing the SQL gives you access to the queries Statsig ran internally, which you can use as a starting point for deeper analysis.
## Use it with replays
MEX works together with [Session Replay](/session-replay/overview) to help you understand the user behavior you measure in your experiments.
================================================================================
# Geotests
URL: https://docs.statsig.com/statsig-warehouse-native/geotests/introduction
Updated: 2026-07-11
================================================================================
Sometimes you cannot run an A/B test at a per-user level. Common causes include:
* You don't have per-user control over who sees what, for example, a third-party marketing platform like Meta or Google Ads.
* You can't attribute metrics to individuals, for example, total store foot traffic can't be tied to which users received your email campaign.
Geotests help solve these problems. One of the most common use cases is marketing: you can't A/B test on third-party ad platforms, but geo-testing lets you evaluate the incrementality of your program.
Geotests are specialized experiments that allow you to target users based on geographic location. These tests enable you to test features, content, or experiences across any type of geographic area you specify. By splitting your campaign across DMAs or postal codes, you can measure incrementality even when an ads platform doesn't support it.
Geotests are particularly valuable for businesses with global audiences who need to optimize their product for regional differences in user needs, preferences, or regulatory requirements.
## Why use Geotests
Statsig's Geotesting combines the flexibility and statistical rigor of Statsig with your own data sources in your warehouse.
Geotesting:
1. Uses the data already in your warehouse, avoiding expensive and time-consuming exports.
2. Includes an automated Experiment Designer to help you select where and when to run your campaign.
3. Exports your campaign definition to your campaign platform, making setup faster and less error-prone.
4. Automates analysis of the campaign, providing statistical rigor to measure incrementality.
## GeoLift
Statsig's Geotesting is built on top of Geolift, a best-in-class industry tool for running geospatial experimentation for marketing. [GeoLift](https://facebookincubator.github.io/GeoLift/) is an open-source package from Meta that’s used by many to infer causal relationships in timeseries data. It builds on advancements made in prior packages like Google’s [Causal Impact](https://google.github.io/CausalImpact/CausalImpact.html) package.
Statsig uses GeoLift in its implementation of Geotesting, allowing you to connect your own internal metrics and feed them into the analysis package.
## Related resources
* [GeoLift](https://facebookincubator.github.io/GeoLift/)
* [CausalImpact](https://google.github.io/CausalImpact/CausalImpact.html)
* [Synthetic Control Methods (wiki)](https://en.wikipedia.org/wiki/Synthetic_control_method)
* [Causal inference using synthetic controls (medium)](https://medium.com/data-science-at-microsoft/causal-inference-using-synthetic-controls-d96a890c83a7)
================================================================================
# Get Started with Geotests
URL: https://docs.statsig.com/statsig-warehouse-native/geotests/geotests-setup
Updated: 2026-07-11
================================================================================
This guide walks you through creating a Geotest experiment using Statsig. The steps include:
1. Define your Geo Types
2. Configure your Metric Source
3. Create and Configure
4. Evaluate Design Options
5. Run Analysis
## Define your geo types
First decide on one or more geo types relevant to your business. Some of the most common are Postal Codes and DMAs (Designated Market Areas), but Statsig allows you to define any arbitrary geo type.
{% figure %}

{% /figure %}
## Configure your metric source
Assuming you've already added a [metric source](/statsig-warehouse-native/configuration/metric-sources/) to Statsig, you can next indicate which column(s) represent the geo type(s) you've created.
{% figure %}

{% /figure %}
## Create and configure
* Navigate to your *Experiments* page.
* Click the **+ Create** button.
* In the modal, select **Analyze** to analyze existing experiment data in your warehouse.
{% figure %}

{% /figure %}
* Enter a name and description for your experiment.
* Choose **Geotest** as the experiment type from the dropdown. Fill in the remaining fields as usual.
{% figure %}

{% /figure %}
In the Setup tab:
* Define your hypothesis.
* Set the Geo Type (e.g., country\_id) from one of the types you've defined in your project and metric sources
* Choose your Primary Metrics.
{% callout type="note" %}
**Important Metric Types**
GeoTest metrics are limited to SUM, COUNT, and COUNT\_DISTINCT types. The inference techniques involved in synthetic control methodologies require this limitation.
{% /callout %}
{% figure %}

{% /figure %}
Configure:
* Expected Treatment Start/End Dates
* Pre-treatment duration (days)
* Effect Size % (the MDE you want to measure; smaller MDEs will make finding a valid design harder)
* Type of Test: 1-sided or 2-sided
* Alpha level (the type I error rate you want, inverse of significance)
* Optionally enable Budgeting
* Additional advanced modeling parameters for users familiar with [Geolift's Advanced API](https://github.com/facebookincubator/GeoLift/blob/main/R/pre_test_power.R)
{% figure %}

{% /figure %}
## Generate design options
When your initial settings are ready, click **Use Experiment Designer** to begin the design process.
{% figure %}

{% /figure %}
The Experiment Designer automates the process of evaluating potential splits of geos. Creating a new design set involves 3 steps:
1. **Define your targeting**
2. **Set any Inclusion or Exclusion overrides**
3. **Define your date range**
{% figure %}

{% /figure %}
### Define targeting
Targeting allows you to define the set of geos eligible to be included in your experiment. By default, Statsig pulls the unique set of geo types from your metric source from the last 90 days. You can add custom filters based on columns in your metric source. For example, you can filter geos to those in a certain region or by transactions that occurred in a certain language.
{% figure %}

{% /figure %}
You can also manually specify a set of geos yourself. The geo id types must match those in your metric source exactly.
### Set inclusion or exclusion overrides
After defining your targeting set, you can manually assign any of these geos to treatment or control groups.
{% figure %}

{% /figure %}
For example, when testing a new marketing campaign, you might want to include Austin because a contract has already been signed, and exclude New York, Los Angeles, or Washington D.C. because of local regulations. Specify these constraints in the designer and all design options considered will conform to these rules.
### Define date range
Define the data range of your design dataset. This date range defines what data is pulled as the historical baseline to train a synthetic control model. A good guideline is that your design data duration should be at least 4x the expected duration of your treatment period, to achieve good fits and maximize sensitivity and power of your experiments.
### Advanced options
* **Rolling Lookback Windows:** Control how many iterative simulations should be done to evaluate power. For instance, a value equal to 5 would simulate power for the last five possible tests in the date range. Corresponds to `lookback_window` in the GeoLift API.
* **Dynamic Time Warping:** Control how much the synthetic control model depends on auto-correlations in the metric vs. correlations between different geos on the same days. A value of 1 focuses exclusively on the metric while a value of 0 (default) relies on correlations only. Corresponds to `dtw` in the GeoLift API.
* **Control Cell Size Requirements:** Sets limits on the share of primary metric value from geos in any considered control group. If not set, Statsig analyzes all design options regardless of their size. For example, if designing on a Revenue metric, a control size range of \[50%, 90%] means that only design options where 50% to 90% of revenue is in the designated control geos. Corresponds to `holdout` in the GeoLift API.
## Evaluate design options
Once a Design Option Set is created (usually takes a few minutes), Statsig shows it in your *Experiment Designs* tab. All design option sets Statsig generates appear here, so you can look through prior design iterations as needed.
{% figure %}

{% /figure %}
Click into a completed design set (e.g. “Design Set 3”).
* Review the ranked recommendations for your best design options
* Compare MDE, Power, Cost and Control/Test Allocation
* Click View Cell Details to expand and see specific geo assignments
* Review additional model performance details
* Select your design using the radio button, then click **Save Design to Experiment**.
{% figure %}

{% /figure %}
## Run analysis
{% figure %}

{% /figure %}
================================================================================
# Geotesting Methodology
URL: https://docs.statsig.com/statsig-warehouse-native/geotests/methodology
Updated: 2026-07-11
================================================================================
## How geotesting differs from standard A/B testing
Standard A/B testing relies on a few core assumptions:
1. You can reliably and randomly split your users into similar groups.
2. While each group has heterogeneity inside it, random user-level variations average out at larger sample sizes, resulting in homologous (similar) groups.
3. You control how each group gets treated, and there's no interference between groups.
When these core assumptions are true, the treatment effect is the only observable difference between the groups. Measure each group’s mean value and find the difference between them.
There are times however when these assumptions don’t hold:
* Your intended treatment can’t be controlled and scoped to the user level
* You don’t know who the users are
* You can’t track outcomes at the user level
Running geographically-based campaigns is a prime example that often features at least some of these violations. Examples include:
* Some classic marketing, like billboards, can’t be randomly split between test and control users at a given location. You can’t track who sees them and how they act afterwards.
* Some digital campaigns might be able to split users 50/50 (if the platform allows it), but without user-level data that can be tied to outcomes of interest, you can’t determine how the two groups performed.
## Using synthetic controls
“Geotesting” as referred to here is an experimental framework that relies on a different basic setup than AB tests:
1. Split your users into geographical boundaries at some useful level (zip codes, states, countries, etc.).
2. Apply a known treatment in some “treatment” geos, and no treatment in some “control” geos.
3. Use the control geos to figure out what would have happened in the treatment geos had no treatment been applied (that is, “synthetic” results).
4. Measure the delta in the treatment geos between the actual and synthetic values.
Determining what would have happened is the most difficult part of this setup. Deducing things that didn't occur is generally unreliable, but in some circumstances there is enough predictability in a system to make it possible.
Geotesting relies on sufficient correlations between control and test geos to make predicting this “counterfactual” possible. General trends and seasonality tend to be shared across some geos.
Example: pretty much all zip codes in the United States exhibit similar quantities of mail sent on any given day. If you were to split zip codes 50/50, you could reasonably infer that the two groups would see similar trends.
## Estimation approach
There are many ways to estimate unknown data from other known data, depending on a multitude of factors. In the case of Geotesting, the data has:
* timeseries data that is autocorrelated (i.e. data is correlated from one day to the next within a group)
* timeseries data that is correlated between groups
* regular seasonal patterns that repeat at regular intervals
* granular data from among many distinct geographies
Plotting a metric of interest for a set of relevant geographies can show these relationships clearly.
{% figure %}

{% /figure %}
Example data from the GeoLift package walkthrough.
But what happens when a campaign runs in only some of these geos? How can you tell if a fluctuation in this graph is random noise or a statistically significant result?
{% figure %}

{% /figure %}
A marketing campaign that starts on day 91 (black dotted line) could have affected the treatment cities of Chicago and Portland. But how do you distinguish any real effect from all the noise?
Causal inference modeling addresses this question. A variety of models and methods have been developed for this purpose. The basic steps are:
* Split your geos into test and control groups.
* Using the pre-treatment period data, train a model on the control data to predict how the treatment geos behave.
* Using the post-treatment period data for the control geos, create a “synthetic control” dataset to predict how the treatment geos would have behaved after the treatment event.
* Subtract the synthetic data from the actual observed data to determine the induced effects of your treatment on the treatment geos.
When your training data is able to produce strong models that can predict metric outcome well, you end up with strong estimates of an induced effect:
{% figure %}

{% /figure %}
{% figure %}

{% /figure %}
Subtracting the modeled Synthetic Control values from the observed Treatment values reveals any incremental effect for the Treatment geos.
================================================================================
# Other Features
URL: https://docs.statsig.com/statsig-warehouse-native/features/other-useful-features
Updated: 2026-07-11
================================================================================
Many features are not listed in the Warehouse Native section because they function nearly identically in both Cloud and Warehouse Native. These are worth exploring as you integrate with Statsig:
* [Feature Gates](/feature-flags/overview) are a best-in-class tool for managing feature release and targeting. Statsig Warehouse Native can automatically analyze feature gates with partial rollouts as experiments.
* [Session Replay](/session-replay/overview) is a powerful tool for viewing how users interact with your product, giving richer context to changes in user behavior
* [Layers](/experiments/layers-overview) allow you to create mutually-exclusive experiments for experiments where changes may collide with other experiments to create a bug or odd result
* [Dynamic Configs](/dynamic-config/working-with) provide server-side configuration tools with optional targeting and integrate naturally with experiments.
* [Admin](/access-management/introduction) tools let you control projects, teams, and user access across Statsig.
================================================================================
# Get started with the Statsig SDK
URL: https://docs.statsig.com/sdks/quickstart
Updated: 2026-07-11
================================================================================
For a more detailed guide, go to the [SDK Overview](/sdks/getting-started) or read about choosing between [client or server SDKs](/sdks/client-vs-server).
{% tabs %}
{% tab title="React" %}
{% steps %}
{% step title="Install Statsig packages" %}
```bash
npm install @statsig/react-bindings
```
{% /step %}
{% step title="Wrap child components" %}
Update your app's default function (typically App.tsx or Layout.tsx) so that the StatsigProvider wraps all child components.
```tsx
import { StatsigProvider } from "@statsig/react-bindings"; // [!code ++]
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
Loading...}> // [!code ++]
{children}
// [!code ++]
);
}
```
{% callout type="note" %}
This example assumes client-side React. For Server-Side Rendering, refer to the [Next.js docs](/client/Next).
{% /callout %}
{% /step %}
{% step title="Add client key" %}
Create a client API key in the [Statsig console Settings](https://console.statsig.com/api_keys). Copy and paste it to replace `` in the code snippet from the previous step.
{% /step %}
{% step title="Basic Usage" %}
{% tabs %}
{% tab title="Check a Gate" %}
First, create a gate on the [Feature Gates page](https://console.statsig.com/gates) in console, then check it in-code:
```jsx
const { client } = useStatsigClient();
return (
Gate is {client.checkGate('check_user') ? 'passing' : 'failing'}.
);
```
{% /tab %}
{% tab title="Get an Experiment Value" %}
First, create an Experiment on the [Experiments page](https://console.statsig.com/experiments) in console
```jsx
const { client } = useStatsigClient();
const experiment = client.getExperiment('my_experiment_name');
return (
);
```
{% /tab %}
{% tab title="Log an Event" %}
Use events to power metrics in your experiment or gates. Events don't need to be set up in console first, just add to your code:
```jsx
const { client } = useStatsigClient();
return
```
{% /tab %}
{% /tabs %}
{% /step %}
{% /steps %}
## Next steps
You've set up the Statsig SDK in React. Continue with the tutorials, or go to the full [Next.js](/client/Next) or [React](/client/React) SDK reference.
{% /tab %}
{% tab title="JS snippet" %}
{% steps %}
{% step title="Paste the code snippet" %}
In the `` section of your website, paste the following code snippet:
```html
```
{% /step %}
{% step title="Add client key" %}
Create a client API key in the [Statsig console Settings](https://console.statsig.com/api_keys). Copy and paste it to replace `` in the code snippet from the previous step.
{% /step %}
{% step title="Basic usage" %}
{% tabs %}
{% tab title="Check a Gate" %}
First, create a gate on the [Feature Gates page](https://console.statsig.com/gates) in console, then check it in-code:
```jsx
window.Statsig.instance().checkGate("my_feature_gate_name");
```
{% callout type="note" %}
Wait for the SDK to initialize before checking a gate to ensure it has fresh values. One way to do this is to wait for the ["values\_updated"](/client/javascript-sdk#client-event-emitter) event.
{% /callout %}
{% /tab %}
{% tab title="Get an Experiment Value" %}
First, create an Experiment on the [Experiments page](https://console.statsig.com/experiments) in console
```jsx
window.Statsig.instance().getExperiment("my_experiment_name").get('my_experiment_parameter_name');
```
{% callout type="note" %}
Wait for the SDK to initialize before getting an experiment to ensure it has fresh values. One way to do this is to wait for the ["values\_updated"](/client/javascript-sdk#client-event-emitter) event.
{% /callout %}
{% /tab %}
{% tab title="Log an Event" %}
Use events to power metrics in your experiment or gates. Events don't need to be set up in console first, just add to your code:
```jsx
window.Statsig.instance().logEvent("my_checkout_event_name", "event_value_item_1234", {"event_metadata": "my_metadata"})
```
{% /tab %}
{% /tabs %}
{% /step %}
{% /steps %}
## Next steps
You've set up the Statsig JavaScript snippet. You can now:
* [Record events](/webanalytics/overview)
* [Watch session replays](/session-replay/overview)
* [Run experiments](/experiments/overview)
* [Use feature flags](/feature-flags/overview)
Go to the [Javascript SDK reference](/client/javascript-sdk) for more information.
{% /tab %}
{% tab title="Python" %}
{% steps %}
{% step title="Install the Statsig package" %}
```shell
pip install statsig-python-core
```
{% /step %}
{% step title="Initialize the Statsig SDK" %}
```python
from statsig_python_core import Statsig, StatsigOptions
options = StatsigOptions()
options.environment = "development"
statsig = Statsig("", options)
statsig.initialize().wait()
statsig.shutdown().wait()
```
{% /step %}
{% step title="Add server secret key" %}
Create a server secret key in the [Statsig console Settings](https://console.statsig.com/api_keys). Copy and paste it to replace `` in the code snippet from the previous step.
{% /step %}
{% step title="Basic Usage" %}
{% tabs %}
{% tab title="Check a Gate" %}
First, create a gate on the [Feature Gates page](https://console.statsig.com/gates) in console, then check it in-code:
```python
user_object = StatsigUser(user_id="123", email="testuser@statsig.com") //add any number of other attributes
gate_value = statsig.check_gate(user_object, "my_feature_gate_name"):
```
{% /tab %}
{% tab title="Get an Experiment Value" %}
First, create an Experiment on the [Experiments page](https://console.statsig.com/experiments) in console
```python
user_object = StatsigUser(user_id="123", email="testuser@statsig.com"
my_experiment_object = statsig.get_experiment(user_object, "my_experiment_name")
my_experiment_parameter_value = my_experiment_object.get_string('my_experiment_parameter_name')
```
{% /tab %}
{% tab title="Log an Event" %}
Use events to power metrics in your experiment or gates. Events don't need to be set up in console first, just add to your code:
```python
user_object = StatsigUser(user_id="123", email="testuser@statsig.com"
statsig.log_event(
user=user_object,
event_name="my_checkout_event_name",
value="SKU_12345"
)
```
{% /tab %}
{% /tabs %}
{% /step %}
{% /steps %}
## Next steps
You've set up the Statsig SDK in Python. Continue with the tutorials, or go to the full [Python SDK Reference](/server-core/python-core).
{% /tab %}
{% tab title="Node" %}
{% steps %}
{% step title="Install the Statsig package" %}
```bash
npm i @statsig/statsig-node-core
```
{% /step %}
{% step title="Initialize the Statsig SDK" %}
```jsx
// Basic initialization
const statsig = new Statsig("");
await statsig.initialize();
// or with StatsigOptions
const options: StatsigOptions = { environment: "staging" };
const statsigWithOptions = new Statsig("secret-key", options);
await statsigWithOptions.initialize();
```
{% /step %}
{% step title="Add server secret key" %}
Create a server secret key in the [Statsig console Settings](https://console.statsig.com/api_keys). Copy and paste it to replace `` in the code snippet from the previous step.
{% /step %}
{% step title="Basic Usage" %}
{% tabs %}
{% tab title="Check a Gate" %}
First, create a gate on the [Feature Gates page](https://console.statsig.com/gates) in console, then check it in-code:
```js
const userObject = new StatsigUser({ userID: "123", email="testuser@statsig.com" });
const is_gate_enabled = statsig.checkGate(userObject, "my_feature_gate_name"):
```
{% /tab %}
{% tab title="Get an Experiment Value" %}
First, create an Experiment on the [Experiments page](https://console.statsig.com/experiments) in console
```js
const userObject = new StatsigUser({ userID: "123", email="testuser@statsig.com" });
const myExperimentObject = statsig.getExperiment(userObject, "my_experiment_name")
const myExperimentParameterValue = myExperimentObject.getValue('my_experiment_parameter_name')
```
{% /tab %}
{% tab title="Log an Event" %}
Use events to power metrics in your experiment or gates. Events don't need to be set up in console first, just add to your code:
```js
userObject = StatsigUser(user_id="123", email="testuser@statsig.com"
statsig.logEvent(
userObject,
"my_checkout_event_name",
"SKU_12345" //value for the event
);
```
{% /tab %}
{% /tabs %}
{% /step %}
{% /steps %}
## Next steps
You've set up the Statsig SDK in Node.js. Continue with the tutorials, or go to the full [Node.js SDK reference](/server-core/node-core).
{% /tab %}
{% tab title="+24 more SDKs" %}
## Explore SDKs
Statsig offers SDKs for a wide variety of platforms:
### Client SDKs
{% card-grid columns="3" %}
{% card title="JavaScript" href="/client/javascript-sdk" icon="js" %}
Browser JavaScript
{% /card %}
{% card title="React" href="/client/React" icon="react" %}
Client-Side React
{% /card %}
{% card title="React Native" href="/client/ReactNative" icon="react" %}
Bare React Native SDK
{% /card %}
{% card title="Next.js" href="/client/Next" icon="n" %}
Next.js SSR, SSG & Client-Side
{% /card %}
{% card title="Angular" href="/client/Angular" icon="angular" %}
Angular bindings for Javascript SDK
{% /card %}
{% card title="Swift" href="/client/iosClientSDK" icon="swift" %}
iOS, MacOS, tvOS SDK
{% /card %}
{% card title="Android" href="/client/Android" icon="android" %}
Android Kotlin/Java SDK
{% /card %}
{% card title=".NET Client" href="/client/DotNet" icon="dotnet" %}
Client SDK for .NET framework
{% /card %}
{% card title="Roku" href="/client/Roku" icon="roku" %}
Roku Brightscript SDK
{% /card %}
{% card title="Unity" href="/client/Unity" icon="unity" %}
Unity game engine SDK
{% /card %}
{% card title="Dart/Flutter" href="/client/Dart" icon="flutter" %}
Flutter/Dart Mobile App SDK
{% /card %}
{% card title="C++ Client" href="/client/CPP" icon="cplusplus" %}
C++ client-side SDK
{% /card %}
{% /card-grid %}
### Server side SDKs
{% card-grid columns="3" %}
{% card title="Node.js" href="/server-core/node-core" icon="node-js" %}
Node.js server SDK
{% /card %}
{% card title="Java" href="/server-core/java-core" icon="java" %}
Java server SDK
{% /card %}
{% card title="Python" href="/server-core/python-core" icon="python" %}
Python server SDK
{% /card %}
{% card title="Go" href="/server/go" icon="golang" %}
Go server SDK
{% /card %}
{% card title="Ruby" href="/server/ruby" icon="ruby" %}
Ruby server SDK
{% /card %}
{% card title=".NET Server" href="/server-core/dotnet-core" icon="dotnet" %}
.NET server SDK
{% /card %}
{% card title="PHP" href="/server-core/php-core" icon="php" %}
PHP server SDK
{% /card %}
{% card title="Rust" href="/server-core/rust-core" icon="rust" %}
Rust server SDK
{% /card %}
{% card title="C++ Server" href="/server-core/cpp-core" icon="cplusplus" %}
C++ server SDK
{% /card %}
{% /card-grid %}
### Integrations
{% card-grid columns="3" %}
{% card title="Webflow" href="/guides/webflow-sidecar-ab-test" icon="w" %}
Webflow integration
{% /card %}
{% card title="Shopify" href="/guides/shopify-ab-test" icon="shopify" %}
Shopify integration
{% /card %}
{% card title="Segment" href="/integrations/data-connectors/segment" icon="segment" %}
Segment data connector
{% /card %}
{% card title="Rudderstack" href="/integrations/data-connectors/rudderstack" icon="rudderstack" %}
Rudderstack connector
{% /card %}
{% card title="Hightouch" href="/integrations/data-connectors/hightouch" icon="hightouch" %}
Hightouch integration
{% /card %}
{% card title="mParticle" href="/integrations/data-connectors/mparticle" icon="mparticle" %}
mParticle connector
{% /card %}
{% card title="Framer" href="/guides/framer-analytics" icon="f" %}
Framer integration
{% /card %}
{% card title="Slack" href="/integrations/slack" icon="slack" %}
Slack notifications
{% /card %}
{% card title="Integrations" href="/integrations/introduction" icon="puzzle" %}
View more integrations
{% /card %}
{% /card-grid %}
{% /tab %}
{% /tabs %}
================================================================================
# Identify Users
URL: https://docs.statsig.com/sdks/identify-users
Updated: 2026-07-11
================================================================================
## Why identify users?
When you run an experiment, roll out a feature, or log events, Statsig needs to know who the user is to determine:
* Targeting: whether a feature gate should pass or fail for a given user
* Experiment bucketing: which group a user belongs to
* Analytics: how many unique users triggered an event
Pass a User Object to Statsig SDKs to provide this information. The User Object is also required for experiment analysis: users are allocated to the test or control group by an ID, and the Statsig stats engine uses the same ID on the users' events to calculate metric differences between test and control groups.
## Basic user object
Start by defining a basic user object:
{% codetabs %}
```jsx Basic User Object
{
"userID": "u_123", // required for most setups
"email": "user@example.com" // optional
}
```
{% /codetabs %}
Add more attributes when you need finer targeting. Refer to [Enriched attributes](#enriched-attributes) for details.
## Identifying users in client SDKs
In client SDKs, the SDK is initialized for a single User Object at any one time. If you call a method like `checkGate` or `getExperiment` before the SDK is initialized, results are incorrect. You'll also see warnings in the Statsig Console on the diagnostics tab of any gate or experiment. Initialization requires a network request, which you start by calling the `initialize()` method, or in React, `useClientAsyncInit()`.
In client apps with asynchronous logic, wait for initialization to complete before checking a gate. For example, in React:
{% codetabs %}
```jsx React Example
export function App() {
const { client, isLoading } = useClientAsyncInit(
YOUR_CLIENT_KEY,
user={ userID: "u_123"} // user object
);
if (isLoading) {
return
Loading...
;
}
return (
);
}
```
{% /codetabs %}
If you initialize with a given user object and the user object changes (for example, the user logs in and now has a `userID`), call the `updateUser` method to refresh values from the Statsig server. For example, in React:
{% codetabs %}
```jsx Update User
const { user, updateUserSync } = useStatsigUser();
return
Current User: { user.userID }
;
```
{% /codetabs %}
If you don't update the user before checking gates or experiments, gates and experiments evaluate using the previous user object, which can produce unexpected results.
Any attribute added to the user object is targetable in the Statsig console. To target users based on `email`, `companyID`, or other attributes, add those attributes to your user object.
## Identifying users in server SDKs
In server SDKs, pass a User Object with each evaluation or event call, such as `logEvent`, `checkGate`, or `getFeatureGate`. The server SDK holds the necessary rules in memory for evaluation. Pass all attributes needed to evaluate your gate or experiment on every call. Statsig SDKs don't store or enrich attributes from previous calls.
Example (Node.js)
{% codetabs %}
```jsx Node.js Example
import { Statsig } from "statsig-node";
await Statsig.initialize("");
const user = { userID: "u_123", custom: { plan: "premium" } };
const gate = await Statsig.checkGate(user, "beta_feature");
if (gate) {
console.log("Gate passed");
}
```
{% /codetabs %}
## Enriched attributes
Statsig enriches the user object with useful attributes to expand targeting options. Examples of what Statsig can enrich:
* `stableID`: an anonymous identifier which identifies a unique device on client SDKs
* `country`: derived from IP or device locale
* `appVersion`: pulled from mobile SDKs
* `browser` / `OS`: captured from user agent
* and more...
To learn about all available fields (including `custom` attributes, `customIDs`, and `privateAttributes`), go to the [StatsigUser object](/sdks/user) docs.
================================================================================
# Create your first feature flag
URL: https://docs.statsig.com/guides/check-gate
Updated: 2026-07-11
================================================================================
This tutorial walks you through how to check your first Feature Gate in Statsig from end to end. Feature Gates, also known as feature flags, are a way to safely control the rollout of new features to your users without deploying additional code. Common examples for using Feature Gates include shipping new UI elements, API endpoints, or product features.
By the end of this tutorial, you have:
* Created a **Feature Gate** in the Statsig console, with **targeting rules** to enable the feature for a segment of Users
* Initialized a **Statsig Client SDK**
* Checked a single **feature gate** in your code using the `checkGate` function
## Prerequisites
1. A [Statsig account](https://console.statsig.com/sign_up)
2. An existing application you can integrate the Statsig Client SDK into
## Step 1: In the Statsig console
### Create a Feature Gate
This example adds a Feature Gate to deploy a new UI element to a user with the "statsig.com" email domain. You can follow along with a specific feature if you have your own scenario in mind.
1. Navigate to [Feature Gates](https://console.statsig.com/gates) in the Statsig console.
2. Then, click on **Get Started** if you don't have any Feature Gates set up yet, or **Create** to create a new one.
3. Name your gate "Example Gate". The SDK also uses this name to identify the Feature Gate later.
4. Enter a description for your Feature Gate. It's good practice to describe it in a way that other teammates can easily understand. For example: "This Feature Gate is for launching an example feature for Statsig employees only."
### Create a targeting rule
When you create a Feature Gate in Statsig, Statsig excludes all users from the feature by default. To enable the feature, add a rule that lets users pass the gate. The steps below walk through adding that rule in the console.
1. In the console, on the page for the Feature Gate you just created, click on **Add New Rule**.
2. Give this rule a **Name**, such as "Statsig Users Only".
3. Select **Email** as the targeting criteria to target users based on their email address.
4. In the User section of the dropdown, select the **Any Of (Case Insensitive)** operator, and then add `statsig.com` for email-based user targeting.
5. Set the **Pass Percentage** to `100%`. Doing so ensures that all users with the `statsig.com` email domain pass the Feature Gate and see the new feature.
6. Click **Add Rule** to add this rule to your Feature Gate.
7. Next, hit **Save** on the bottom right to commit these changes to the Feature Gate.
You can now test this feature gate by configuring the User object in the "Test Gate" section.
### Create a Client API Key
Now that you've set up the Feature Gate from the console, it's time to integrate it into your product with the Statsig SDK. First, create a new Client API key to use in your product.
1. Navigate to [**Keys & Environments**](https://console.statsig.com/api_keys) in the Statsig console. You can also find this by going to **Settings** at the bottom left of the Statsig console.
2. Scroll down to **API Keys**. Click on **Generate New Key**.
3. In the dropdown, select **Client**.
4. Copy the Client API Key you just created to your clipboard.
## Step 2: In your code
### Initialize the Statsig SDK
Now that you have a Client API Key, integrate the Statsig Client SDK into your product. This tutorial uses the React SDK, but you can use a different SDK if you prefer.
{% callout type="tip" %}
Statsig offers over 20 client and server-side SDKs. Check out the full list of [SDKs](/sdks/quickstart#all-sdks) to find the one that best fits your needs.
{% /callout %}
1. Install the Statsig React SDK using your preferred package manager. This tutorial uses npm.
{% codetabs %}
```bash npm
npm install @statsig/react
```
{% /codetabs %}
2. Import the SDK in your `App.js` file:
{% codetabs %}
```tsx Import SDK
import { StatsigProvider } from "@statsig/react-bindings";
```
{% /codetabs %}
3. Wrap your app's content within the `StatsigProvider` component. The following code snippet also creates a [User](/sdks/user) object for targeting your Feature Gate.
{% codetabs %}
```tsx Setup Provider
function App() {
return (
Hello world
);
}
export default App;
```
{% /codetabs %}
4. Make sure to also replace `client-KEY` with the Client API Key you copied in Step 3.
### Check your Feature Gate
Evaluate a Feature Gate in your product code by getting the client with the `useStatsigClient` hook and calling `checkGate`. If you aren't sure where to place your flag, refer to [Best Practices for feature flags](/feature-flags/best-practices).
1. Add the following code to your `App.js` file. In this snippet, the `example_gate` is the name of the Feature Gate you created in Step 1.
{% codetabs %}
```tsx Check Gate
const { client } = useStatsigClient();
return (
Gate is {client.checkGate('check_user') ? 'passing' : 'failing'}.
);
```
{% /codetabs %}
2. Run your app. The app renders "Gate is passing" because the [targeting rule](#create-a-targeting-rule) targets all users with the `statsig.com` email domain, and this client's User object uses that same domain.
3. After you set up your gate, [monitor the impact of your new feature rollout](/feature-flags/view-exposures) or [manage flag lifecycles](/feature-flags/feature-flags-lifecycle).
## Run your first A/B test
This tutorial configured a simple feature flag. To do more complex feature rollouts or metric analysis, continue to the next tutorial to run your first A/B test in Statsig.
================================================================================
# Run your first A/B test
URL: https://docs.statsig.com/guides/abn-tests
Updated: 2026-07-11
================================================================================
In this guide, you create and implement your first experiment in Statsig from end to end. There are many types of experiments you can set up in Statsig. This guide walks through the most common one: an A/B test.
By the end of this tutorial, you have:
* Created a new user-level **Experiment** in the Statsig console, with **parameters** set for a Control and Experiment group
* **Checked the experiment** in your application code using the **Statsig Client SDK** `getExperiment` function
## Prerequisites
1. You already have a [Statsig account](https://console.statsig.com/sign_up)
2. You already [installed the Statsig Client SDK](/sdks/quickstart) in an existing application
## Step 1: In the Statsig console
### Create an experiment
1. Log in to the Statsig console at https://console.statsig.com/ and navigate to Experiments in the left-hand navigation panel.
2. Click on the **Create** button and enter the name `Quickstart Experiment` and enter a brief hypothesis. For example, "A new homepage banner will improve engagement."
3. Click **Create**.
### Set up scorecards
In the Experiment Setup tab, fill out scorecards for the metrics you want to track. For this SDK tutorial, leave most settings at their default values.
1. In the **Hypothesis** card, enter a hypothesis for what you are testing: "Showing a homepage banner will increase user engagement, measured by daily\_stickiness."
2. In the **Primary Metrics** card, add a new metric called `daily_stickiness`.
3. Leave the **Secondary Metrics** and **Duration** settings at their default values. For more details on experiment setup, refer to [Creating an experiment](/experiments/create-new).
### Configure groups and parameters
By default, the experiment runs on 100% of users: Statsig assigns 50% to a Control group and 50% to an Experiment group.
1. In the **Groups and Parameters** card, create a parameter called `enable_banner` and select the type as `Boolean`.
2. Set `enable_banner` to `false` for the Control group and `true` for the Experiment group.
3. Click **Save**.
4. Select **Save** in the bottom right to finalize this experiment setup.
5. The experiment isn't live yet. To launch it, select **Start** at the top of the page. This makes production traffic eligible for the experiment.
{% callout type="note" %}
You can't configure experiment parameters and groups after launching an experiment. To test in a staging environment, refer to [using environments](/guides/using-environments/#configuring-environments).
{% /callout %}
## Step 2: In your application code
### Check the experiment
{% callout type="note" %}
This tutorial assumes you have already [installed the Statsig Client SDK](/sdks/quickstart) in an existing application.
{% /callout %}
Use a Statsig Client SDK to check a user's assigned experiment group and parameters in real time. The SDK changes the user's experience according to the assigned variant. In this case, fetch the value of the `enable_banner` parameter created in Step 1.
{% codetabs %}
```tsx Check Experiment
const quickstartExperiment = myStatsigClient.getExperiment(user, "quickstart_experiment");
// the second parameter is the default fallback value if the experiment is not found
if (quickstartExperiment.get("enable_banner", false)) {
showBanner();
}
```
{% /codetabs %}
In this snippet, users assigned to the Experiment group see the banner. Users assigned to the Control group don't.
You don't need to [hardcode the experiment group name](/experiments/implementation/getting-group) ("Control" or "Experiment") in the code. The Statsig SDK automatically fetches the experiment configuration and returns the correct value based on the user's variant.
## Step 3: Monitor experiment diagnostics
When you run your code, the SDK calls the `showBanner()` function for users in the Test group. Navigate to your experiment in the Statsig console and select the **Diagnostics** tab to see a live log stream of checks and events from your application.
Refer to the [Diagnostics tab documentation](/experiments/implementation/getting-group#rules) for more information.
{% figure %}

{% /figure %}
## Step 4: Read experiment results
Within 24 hours of starting your experiment, the **Results** tab in the Statsig console shows cumulative exposures and their impact on the scorecard metrics configured in the **Setup** panel.
Here's a sample of what that looks like:
{% figure %}

{% /figure %}
## Log custom events and metrics
In this tutorial, you used `daily_stickiness` as the primary metric. Statsig logs this metric automatically, so you don't need to log it explicitly.
To measure other events or custom metrics that occur after the experiment check, log them to Statsig. Continue to the next tutorial to learn how to log custom events and metrics with the Statsig SDK.
### Related guides
* [Initialization techniques for Statsig SDKs](/client/concepts/initialize)
* [Run a device-level experiment](/guides/first-device-level-experiment)
* [Run an experiment on custom unit ID types](/guides/experiment-on-custom-id-types)
* [Configure the Statsig SDK for staging environments](/guides/using-environments)
================================================================================
# Log your first custom event
URL: https://docs.statsig.com/guides/logging-events
Updated: 2026-07-11
================================================================================
Tracking events is the foundation of building better products. Whether you plan to use Statsig for web analytics, product analytics, or experimentation, defining the key events and metrics for your product is the best place to start. You can view event data in [Metrics Explorer](/product-analytics/overview), turn it into [custom metrics](/metrics/how-metrics-work), visualize it in [dashboards](/product-analytics/dashboards), and use it in [experiment results](/metrics/pulse) to show the impact of your experiments.
Statsig provides several ways to get started:
1. Statsig web SDKs offer [autocaptured web analytics](/webanalytics/overview) to automatically log common events like pageviews, clicks, and more.
2. Statsig offers [integrations](/integrations/introduction) to connect your existing event data in Segment, mParticle, and other sources.
3. All Statsig client and server SDKs provide a simple `logEvent` API to instrument your own events in your app or webserver.
For general guidance on event logging and core concepts, read on or navigate to the "Logging events via SDKs" section below.
## Identifying users with the StatsigUser object
Many analytics platforms have a concept of "identifying" a user. In Statsig, this is the `StatsigUser` object, which is set at initialization time in client SDKs, or with each event in server SDKs.
The [`StatsigUser`](/sdks/user) is a set of properties that describe the user. The JSON definition is consistent across all SDKs and integrations:
{% codetabs %}
```json StatsigUser Object
{
"userID": "123",
"customIDs": {},
"email": "user@example.com",
"ip": "192.168.1.1",
"userAgent": "Mozilla/5.0...",
"country": "US",
"locale": "en-US",
"appVersion": "1.0.0",
"custom": {},
"privateAttributes": []
}
```
{% /codetabs %}
The `userID` field is reserved for a unique identifier for the user. Use the ID of the logged-in user.
`customIDs` are explained in the "Group Analytics" section below.
The other fields support targeting and evaluation for feature flags, and can also be used for custom metrics and data queries in Metrics Explorer.
## Group analytics
Another core concept in analytics is grouping. A user can belong to a company, organization, page, or other entity.
In Statsig, groups are represented by the `customIDs` field, a dictionary that can contain multiple IDs for a single user. For example, a user can have the following `customIDs`:
{% codetabs %}
```json Group Example
{
"userID": "123",
"customIDs": {
"companyID": "456",
"projectID": "abc"
}
}
```
{% /codetabs %}
Statsig computes all metrics at the user level and also for each custom identifier. This means you can run experiments at a company level, where all users in a company receive the same experience, and compare the impact on company-level metrics.
You can also use Metrics Explorer to slice metrics at the company level rather than the user level, if you have configured a `customID` for it. For example, the Statsig project can display console page views at a user or company level:
{% figure %}

{% /figure %}
To set up a new "group" or "customID":
1. Go to your [project settings](https://console.statsig.com/settings)
2. Locate the "Custom IDs" section and hit "edit"
{% figure %}

{% /figure %}
3. Name the new `customID` and add a description so other members of your project understand its purpose
4. Start logging events with that customID in the `customIDs` field of the StatsigUser object
{% callout type="note" %}
You must provide the set of all IDs on each StatsigUser object.
{% /callout %}
## Best practices
### Set all known IDs on each StatsigUser object
To generate metrics for each user and group, Statsig requires all IDs on each `StatsigUser` object. Pass all identifiers for the user at client SDK initialization time or when calling `logEvent` in a server SDK, so each identifier contributes to the correct metrics.
If you use a logged-out identifier for anonymous users, continue passing that identifier after the user logs in and you populate `userID`. This allows gates or experiments targeting the logged-out identifier to continue bucketing the user correctly and enables metrics to be calculated at both the logged-out and logged-in levels.
### Deduplicating custom events
Statsig doesn't provide a way to deduplicate custom events that you log.
### Naming conventions
Consistency is the most important aspect of event naming. An inconsistent events catalogue becomes difficult to navigate. Statsig accepts any naming convention (for example, `Page View`, `PageView`, `Page-view`, or `page_view`), but the recommended format is `page_view`: lowercase, no spaces. You can't use special characters in event names. Statsig drops events that contain the following character set: `"\\[\]{}<>#=;&$%|\u0000\n\r"`. Using the recommended convention avoids duplicate entries with different casing and ensures compatibility with downstream systems connected through integrations.
### What to measure
For experimentation and product analytics, log events that correspond to user actions. How you classify events depends on your goals:
1. Avoid high-cardinality event names if you have a small user base, because sparse data reduces the reliability of metrics. For example, an eCommerce website should use a generic `product_page_view` event with contextual page information in the `optional_event_metadata` object, rather than a separate event name for every product page.
2. If you have a well-defined user funnel, log each step as a separate event.
3. If you have a less defined user journey, log generic events (for example, `page_view`) and add more detail to the value field (the primary dimension of interest) or the `optional_event_metadata` object.
## Logging events through SDKs
All Statsig SDKs provide an API to log events. The API looks like this:
{% codetabs %}
```js JavaScript
statsig.logEvent(
event_name,
optional_event_value,
optional_event_metadata
);
```
{% /codetabs %}
Where event name describes a notable event in your product, like `sign_up`, `achievement_unlocked`, `add_to_cart`, `check_out`, etc.
Events can optionally include an event value: for example, the type of achievement unlocked, the name of the product added to cart, or the price of the item purchased. In experiments, the value field generates an automatic breakdown of the event when there are fewer than 8 distinct values.
The metadata field lets you specify additional details about the event. The following examples show how a `logEvent` call looks:
{% codetabs %}
```js JavaScript Example
statsig.logEvent(
'add_to_cart', // Name
19.99, // Price
{
item_id: 'BC22010',
cart_size: '2',
user_segment: 'first_time_purchaser',
}
);
```
```csharp C# Example
StatsigClient.LogEvent(
"level_completed", // Event Name
11, // Level number
new Dictionary()
{
{ "score", "452" }
}
);
```
{% /codetabs %}
{% callout type="info" %}
**Size limits on event payload**
There are limits to how large each event field can be.
Object fields have an overall limit of 4096 on its stringified length.
String fields have a limit of 64 on its length.
{% /callout %}
Refer to the [SDK reference](/sdks/getting-started) for more details on logging events in your language.
================================================================================
# SDK Overview
URL: https://docs.statsig.com/sdks/getting-started
Updated: 2026-07-11
================================================================================
Statsig's SDKs are the in-code tool you'll use to show experiment variants, flag features, and log key business metrics. Statsig's SDKs:
* **Abstract the complexity:** Statsig handles caching, retry mechanisms, and networking.
* **Encourage best practices:** Built around parameters and language-specific best practices.
* **Broad deployment support:** 30+ SDKs across clients, servers, the edge, and more.
***
## When to use Statsig SDKs
### 1. Targeting and assignment
Decide in code who sees new features and experiment variants. Target based on any **user attributes** (for example, location or device type) or **environment-level attributes** to control rollouts and experiment enrollment.
### 2. Logging events
Log key business metrics to power experiment and product analytics. Events flow into Statsig automatically. For web-based platforms (for example, JavaScript, React), Statsig also supports **[Autocaptured events](/webanalytics/overview)**.
***
## Choose between client and server SDKs
Statsig offers both **client-side** and **server-side** SDKs, each suited to different use cases:
* **Client-side SDKs**: Designed for user-facing applications where events are logged directly from the browser or mobile app. These SDKs operate in real time and provide instant feedback on user behavior.
* **Server-side SDKs**: Designed for backend services. Use server-side SDKs to control experiments, feature flags, and log server-side events. Server-side SDKs provide more control for use cases involving system-level actions or business logic.
For a detailed comparison, go to the [Client vs Server SDK Overview](/sdks/client-vs-server).
For frameworks like **Next.js** that bridge client and server-side logic, go to the [Next.js SDK](/client/Next) guide.
***
## Available SDKs
Statsig offers SDKs for a wide variety of platforms:
### Client SDKs
{% card-grid columns="3" %}
{% card title="JavaScript" href="/client/javascript-sdk" icon="js" %}
Browser JavaScript
{% /card %}
{% card title="React" href="/client/React" icon="react" %}
Client-Side React
{% /card %}
{% card title="React Native" href="/client/ReactNative" icon="react" %}
Bare React Native SDK
{% /card %}
{% card title="Next.js" href="/client/Next" icon="n" %}
Next.js SSR, SSG & Client-Side
{% /card %}
{% card title="Angular" href="/client/Angular" icon="angular" %}
Angular bindings for Javascript SDK
{% /card %}
{% card title="Swift" href="/client/iosClientSDK" icon="swift" %}
iOS, MacOS, tvOS SDK
{% /card %}
{% card title="Android" href="/client/Android" icon="android" %}
Android Kotlin/Java SDK
{% /card %}
{% card title=".NET Client" href="/client/DotNet" icon="dotnet" %}
Client SDK for .NET framework
{% /card %}
{% card title="Roku" href="/client/Roku" icon="roku" %}
Roku Brightscript SDK
{% /card %}
{% card title="Unity" href="/client/Unity" icon="unity" %}
Unity game engine SDK
{% /card %}
{% card title="Dart/Flutter" href="/client/Dart" icon="flutter" %}
Flutter/Dart Mobile App SDK
{% /card %}
{% card title="C++ Client" href="/client/CPP" icon="cplusplus" %}
C++ client-side SDK
{% /card %}
{% /card-grid %}
### Server side SDKs
{% card-grid columns="3" %}
{% card title="Node.js" href="/server-core/node-core" icon="node-js" %}
Node.js server SDK
{% /card %}
{% card title="Java" href="/server-core/java-core" icon="java" %}
Java server SDK
{% /card %}
{% card title="Python" href="/server-core/python-core" icon="python" %}
Python server SDK
{% /card %}
{% card title="Go" href="/server/go" icon="golang" %}
Go server SDK
{% /card %}
{% card title="Ruby" href="/server/ruby" icon="ruby" %}
Ruby server SDK
{% /card %}
{% card title=".NET Server" href="/server-core/dotnet-core" icon="dotnet" %}
.NET server SDK
{% /card %}
{% card title="PHP" href="/server-core/php-core" icon="php" %}
PHP server SDK
{% /card %}
{% card title="Rust" href="/server-core/rust-core" icon="rust" %}
Rust server SDK
{% /card %}
{% card title="C++ Server" href="/server-core/cpp-core" icon="cplusplus" %}
C++ server SDK
{% /card %}
{% /card-grid %}
***
## Install your SDK
1. **Select your SDK**: Choose the client or server SDK that fits your platform from the lists above.
2. **Follow the installation guide**: Each SDK has an installation guide that walks you through setup.
3. **Start experimenting**: After you integrate the SDK, set up feature flags, run experiments, and log events for analysis.
If you have questions or need help with installation, reach out in the [Slack community](https://statsig.com/slack).
================================================================================
# User (StatsigUser) Object
URL: https://docs.statsig.com/sdks/user
Updated: 2026-07-11
================================================================================
## Introduction to the StatsigUser object
The StatsigUser object is the sole input you provide to SDKs to target gates and assign users to experiments. To target on an attribute, add it to your user object. Assembling this data is an important part of SDK setup. Provide as much information as practical, because each additional field can enrich your analyses and expand targeting options. Statsig also infers some information about each user from other traits (for example, Statsig resolves IP addresses into countries).
## Usage example in Node.js
{% codetabs %}
```jsx Node.js Example
//Create a user object
const user = new StatsigUser({ userID: "12345", email: "vincent@statsig.com"});
//Use it in getExperiment()
const my_experiment = statsig.getExperiment(user, "my_experiment_name") //<- any attribute you pass, you can target on
```
{% /codetabs %}
### I passed that attribute before: why do I need to pass it again?
You can view a specific user's historical attributes for analytics purposes through [User Profiles](/product-analytics/users-tab) in the Statsig console.
Historical attributes can't be used for targeting or gate/experiment evaluation at runtime. Statsig evaluates gates and experiment buckets based **only on the information you provide at the time of the check.** Millisecond evaluation performance depends on having all information available at request time, rather than querying historical data or storing large amounts of state in memory.
## User attributes
{% tabs %}
{% tab title="Client SDKs" %}
All user attributes can be explicitly supplied, and some can be inferred from a user's device or connection. Supplying one always overrides an inferred value.
| Key | Description | Example | Client SDK Support | Auto-infer |
|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------|--------------------|------------|
| `userID` | ID representing a unique user. Statsig uses this ID to guarantee targeting consistency for Feature Gates and Experiments and to evaluate experiment results. If `User ID` doesn't exist yet, leave this empty; Statsig uses a `Stable ID` persisted locally for evaluations. | `your_user_id` | All | |
| `email` | Email of the user. | `marcos@statsig.com` | All | |
| `userAgent` | User agent of the browser. The SDK decodes this to determine the Browser and Operating System of the user's context. The SDK infers this if not provided. | `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.40 Safari/537.36` | Web | ✔ |
| `ip` | IP address of the user. Inferred from the request to /initialize if not provided | `192.168.1.101` | All | ✔ |
| `country` | 2-letter country code of the user. This can be supplied or inferred, and you can target based on the country code in both cases. When inferred, the country code follows ISO-3166. | `US` | All | ✔ |
| `locale` | Locale of the user. When using the Android or iOS SDK, the SDK infers this if not provided. | `en_US` | Mobile | ✔ |
| `appVersion` | Version of the app the user is using. When using the Android or iOS SDK, the SDK infers this if not provided. | `1.0.1` | Mobile | ✔ |
| `systemName` | When using the Android/iOS SDKs, the SDK automatically assigns this, but you can also provide an explicit operating system to override. | `Android` | All | ✔ |
| `systemVersion` | When using the Android/iOS SDKs, the SDK automatically assigns this, but you can also provide an explicit OS version to override. | `15.4` | All | ✔ |
| `browserName` | When using the Web SDK, the SDK automatically assigns this, but you can also provide an explicit Browser Name to override. | `Chrome` | Web | ✔ |
| `browserVersion` | When using the Web SDK, the SDK automatically assigns this, but you can also provide an explicit Browser Version to override. | `45.0` | Web | ✔ |
| `custom` | Dictionary that can contain key/value pairs that can be used for Feature Gate targeting. Statsig stores this dictionary and makes it available after targeting. | `{subscriber: "yes", ...}` | All | |
| `privateAttributes` | Dictionary that can contain key/value pairs that can be used for Feature Gate targeting. Statsig does **not** store this dictionary after using it for targeting, and removes it from any `log_event` calls. | `{sensitive_field: "sensitive_information", ...}` | All | |
| `customIDs` | Dictionary that can contain key/value pairs used as the randomization unit ID for experiments that are set up using these IDs instead of the `User ID`. | `{account_id: "23456555", company_id: "company_xyz"}` | All | |
### How to override the stable ID
Client SDKs generate a `stableID` for logged-out or device-level targeting. If you already manage a durable device identifier, override the SDK-generated value before or during initialization so Statsig uses your identifier for stable ID-based evaluations.
{% tabs %}
{% tab title="JavaScript" %}
```tsx
import { StatsigClient, StatsigUser } from '@statsig/js-client';
const user: StatsigUser = {
userID: 'a-user',
customIDs: {
stableID: 'my-custom-stable-id',
},
};
const client = new StatsigClient('client-sdk-key', user);
await client.initializeAsync();
```
{% /tab %}
{% tab title="Android" %}
```kotlin
val options = StatsigOptions(overrideStableID = "my-custom-stable-id")
Statsig.initialize(app, "client-sdk-key", user, options = options)
```
{% /tab %}
{% tab title="iOS" %}
```swift
let options = StatsigOptions(overrideStableID: "my-custom-stable-id")
Statsig.initialize(sdkKey: "client-sdk-key", user: user, options: options)
```
{% /tab %}
{% /tabs %}
{% callout type="note" %}
When you override the Stable ID in a client SDK, the SDK persists that value locally and reuses it on later sessions unless local storage is cleared or the app is reinstalled.
{% /callout %}
{% /tab %}
{% tab title="Server SDKs" %}
All user attributes can be explicitly supplied, and some can be inferred from a provided IP Address or User Agent. Supplying one always overrides an inferred value.
| Attributes | Description | Example | Auto Infer |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |------------|
| `userID` | ID representing a unique user. Statsig uses this ID to guarantee targeting consistency for Feature Gates and Experiments and to evaluate experiment results. | `your_user_id` | |
| `email` | Email of the user | `marcos@statsig.com` | |
| `userAgent` | User agent of the browser. The SDK decodes this to determine the Browser and Operating System of the user's context | `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.40 Safari/537.36` | |
| `ip` | IP address of the user | `192.168.1.101` | |
| `country` | 2 letter country code of the user | `US` | ✔, from IP |
| `locale` | Locale of the user | `en_US` | ✔, from IP |
| `appVersion` | Version of the app the user is using | `1.0.1` |
| `custom` | Dictionary that can contain key/value pairs that can be used for Feature Gate targeting. Statsig stores this dictionary and makes it available after targeting | `{skill_level: "5", is_subscriber:"false" ...}` |
| `privateAttributes` | Dictionary that can contain key/value pairs that can be used to evaluate feature gate conditions and segment conditions. Statsig does **not** store this dictionary after using it for targeting, and removes it from any log\_event calls | `{sensitive_field: "sensitive_information", ...}` |
| `customIDs` | Dictionary that can contain key/value pairs used as the randomization unit ID for experiments that are set up using these IDs instead of the `User ID` | `{account_id: "23456555", company_id: "company_xyz"}` |
### How to override "Operating System" and "Browser" explicitly
Operating system and browser are two default targeting options in Statsig. When you set the `userAgent` field, server SDKs parse the OS and browser information from it. To set these values explicitly, provide them either at the top level of the user object (which some language type systems may not allow) or inside the `custom` object, using these keys:
* Operating System: os\_name
* OS Version: os\_version
* Browser Name: browser\_name
* Browser Version: browser\_version
You can set these values either of the following ways in the user object:
{% codetabs %}
```json Example
{
"userID": "uuid",
"os_name": "Android", // top level
"custom": {
"os_name": "iOS"
}
}
```
{% /codetabs %}
If you explicitly set either of these fields, it takes precedence over the value inferred from the `userAgent` field.
{% /tab %}
{% /tabs %}
### Have sensitive user PII data that shouldn't be logged?
The StatsigUser object includes a `privateAttributes` field, which is a key/value dictionary for setting private user attributes. Statsig uses attributes in `privateAttributes` only for evaluation and targeting, and removes them from any logs before sending them to the Statsig server.
For example, if you have feature gates that should only pass for users with emails ending in "@statsig.com" but don't want to log email addresses to Statsig, add the key-value pair `{ email: "my_user@statsig.com" }` to `privateAttributes` on the user.
### Time-based conditions
All SDKs (server and client-side) support Unix timestamps in milliseconds to evaluate time-based conditions (After time, Before time). Convert your DateTime field to a standard format before evaluation.
Statsig has added ISO timestamp support to some server SDKs:
* `java-server` SDK v1.6.0 and later: supports [DateTime fields in `ISO_INSTANT` format](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ISO_INSTANT)
* `go` SDK v1.12.1 and later: supports [DateTime fields in `RFC3339` format](https://pkg.go.dev/time#pkg-constants)
* `python` SDK: supports epoch time in seconds. The `time.time()` function may include sub-second components, so round the value to an integer.
For server SDKs supported by server core (Java, Python, PHP, Rust, Node), ISO timestamp support covers:
* [DateTime fields in `ISO_INSTANT` format](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ISO_INSTANT)
* [DateTime fields in `RFC3339` format](https://pkg.go.dev/time#pkg-constants)
### Why is an ID always required for server SDKs?
In server SDKs, a StatsigUser with a `userID` (or `customID`) is required for `checkGate`, `getConfig`, and `getExperiment`. Pass the user ID whenever it's available: users get a stable experience, and events are attributed to the correct users so you can accurately measure downstream metrics.
Guidance for cases where you can't pass an ID:
1. **On/off feature gates or non-percent-based rules (such as country targeting):** Pass any non-empty identifier or a hardcoded string. If you do not have the actual user ID, use **a random ID under 100** as a placeholder. Do not use a purely random ID, because this prevents event deduplication, inflates event volume, and increases your Statsig bill.
2. **Partial rollout with regression checks before full rollout:** Pass an ID in every `checkGate`, `getConfig`, and `getExperiment` call, as well as in any `logEvent` calls. Without an ID, Statsig cannot attribute logged events to the correct users or calculate metrics accurately.
3. **A/B experiment to decide whether to ship a feature:** Pass persistent user IDs, for the same reason as case 2.
4. **No logged-in user (for example, optimizing the login flow):** Set a stable identifier as a cookie or in local storage and pass it with each Statsig call. Client SDKs provide a stable ID automatically.
For use cases not covered here, reach out in the [Slack community](https://statsig.com/slack).
### Pass all IDs when you have them
A common mistake is accidentally exposing users without a `userID` to a user-ID-based experiment. All such users are bucketed into the same group (wherever the null `userID` hashes to), which skews results. If you run experiments on multiple ID types, pass every available identifier.
================================================================================
# Initializing SDKs
URL: https://docs.statsig.com/client/concepts/initialize
Updated: 2026-07-11
================================================================================
The first step in using a Statsig SDK is calling `initialize()`, which retrieves the values needed to evaluate experiments and send events. Before initialization, Statsig SDKs don't have the latest values in memory and may return stale values or none at all.
Unlike Server SDK initialization, which happens at server startup, Client SDK initialization happens when a screen is rendered, so initialization has more impact on user experience. Statsig offers several client initialization methods to tune performance to your needs.
{% tabs %}
{% tab title="Client SDKs" %}
## General Initialization Flow
`initialize` will take an SDK key and `StatsigUser` object. The SDK will then:
1. Check local storage for cached values. The SDK caches previous evaluations locally so they are available in the next session if there is no successful network call.
2. Create a `STATSIG_STABLE_ID`: an ID that stays consistent per device, which is useful for logged-out experiments.
3. Set the SDK as initialized so checks don't throw errors. Checks return cached values or defaults.
4. Issue a network request to Statsig to get the latest values for all gates/experiments/configs/layers/autotunes for the given user. If the project definition hasn't changed from the most recent cached values, this request may succeed without returning new data.
5. Resolve the asynchronous `initialize` call. If the request to the server failed, the SDK uses cached values or returns defaults for this session.
Depending on when you check a gate or experiment after initializing, fresh values may not be available yet. Awaiting initialization resolves this, with some performance tradeoffs (discussed below).
## Client Initialization Strategies
Below are the various strategies summarized at a high level, ordered from most common to least common:
* [**Asynchronous Initialization (Awaited)**](#1-asynchronous-initialization-awaited): Wait for the initialization network call to finish before rendering content.
* [**Bootstrap Initialization**](#2-bootstrap-initialization): Generate the assignment values on your own server, and pass them down with other request, resulting in zero-latency rendering. Best of both worlds for latency and availability of fresh assignments, but requires additional engineering effort.
* [**Asynchronous Initialization (Not Awaited)**](#3-asynchronous-initialization-not-awaited): Do not await the return of the initialization network call. This ensures immediate rendering, but in a state that reflects stale assignments or no assignments available.
* [**Synchronous Initialization**](#4-synchronous-initialization): Renders immediately, but with stale or no assignments available. First-visit users will never be assigned to gates and experiments.
| Method | Speed-to-render? | Render consistency? | Latest content? | Engineering Complexity? |
|-------------------------|---------------------|-----------------|-----------------|------------------|
| **Explanation** | When I visit the webpage, how fast does the content appear? | Does the content ever change/flicker? | Does the user ever see an out-of-date config value? | How easy is this to implement? |
| Await InitializeAsync() | ❌ Slow | ✅ Good | ✅ Yes | ✅ Easy |
| InitializeAsync() | ✅ Fast | ❌ Poor | ✅ Yes | ✅ Easy |
| InitializeSync() | ✅ Fast | ✅ Good | ❌ No | ✅ Easy |
| BootstrapInit | ✅ Fast | ✅ Good | ✅ Yes | ❌ Extra Effort |
### 1. Asynchronous Initialization - Awaited
> Ensures latest assignments but requires a loading state
When calling `StatsigClient.initializeAsync`, the client loads values from the cache and fetches the latest values from the network. This approach waits for the latest values before rendering. It isn't immediate, but ensures the values are up to date.
{% codetabs %}
```tsx React Example
const { client, isLoading } = useClientAsyncInit(
YOUR_CLIENT_KEY,
{ userID: "u_123" }
);
if (isLoading) {
return
Loading...
;
}
// Continue with initialized client
```
```js JavaScript Example
const client = new StatsigClient(YOUR_CLIENT_KEY, { userID: "u_123" });
await client.initializeAsync();
// Client is now initialized with latest values
```
{% /codetabs %}
### 2. Bootstrap Initialization
> Ensures both latest assignments with no rendering latency
Bootstrapping allows you to initialize the client with a JSON string. Values are immediately available without the client making any network requests. You are responsible for keeping these values up to date.
Your server serves the configuration payload to your client app on page load (for web implementations) or during app launch (for mobile implementations).
{% codetabs %}
```tsx React Bootstrap
// Server-generated initialization values
const initValues = getStatsigValuesFromServer(user);
const client = useClientBootstrapInit(
YOUR_CLIENT_KEY,
{ userID: "u_123" },
initValues
);
// Client renders immediately with server values — no network request
```
```js JavaScript Bootstrap
const bootstrapValues = getInitializationValuesFromServer();
const client = new StatsigClient(YOUR_CLIENT_KEY, { userID: "u_123" });
client.dataAdapter.setData(bootstrapValues);
client.initializeSync();
```
{% /codetabs %}
### 3. Asynchronous Initialization - Not Awaited
To fetch the latest values without awaiting the asynchronous call, call `initializeAsync` and catch the promise. This approach provides immediate rendering with cached values initially, then updates to the latest values mid-session.
{% callout type="warning" %}
Be aware that the values may switch when checked a second time after the latest values have been loaded.
{% /callout %}
### 4. Synchronous Initialization
> Ensures immediate rendering but uses cached assignments (when available)
When calling `StatsigClient.initializeSync`, the client uses cached values if they are available. New values are fetched in the background and the cache is updated. This approach provides immediate rendering, but values may be stale or absent during the first session.
{% /tab %}
{% tab title="Server SDKs" %}
## General Initialization Flow
Server SDKs require only a secret key to initialize. Because servers handle many users, server SDKs download all rules and configurations in your project and evaluate them in real time for each user. The Server SDK initialization process:
1. Your server checks if you have locally cached values (which you can set up with a [DataAdapter](/server/concepts/data_store/)).
2. If your server found values on the last call, it's ready for checks with the reason "DataAdapter". Whether it found local data or not, it next goes to the network to find updated values.
3. The server retrieves updated rules from the network, and is now ready for checks even if it didn't find values in step 1.
4. Going forward, the server retrieves new values every 10 seconds from the network, updating the locally cached values each time.
DataAdapters provide resilience and ensure your server is ready to serve requests as soon as it starts up, without waiting for a network round trip. This is especially useful for short-lived or serverless instances. For advanced setups, Statsig offers a [Forward Proxy](/server/concepts/forward_proxy/) for additional resilience.
{% codetabs %}
```js Node.js Initialization
import { Statsig, StatsigUser } from '@statsig/statsig-node-core';
const statsig = new Statsig("YOUR_SERVER_KEY");
await statsig.initialize();
// Server SDK is now ready to evaluate gates/experiments for any user
const user = new StatsigUser({ userID: "123" });
const gate = statsig.checkGate(user, "my_gate");
```
```python Python Initialization
from statsig_python_core import Statsig, StatsigUser
statsig = Statsig("YOUR_SERVER_KEY")
statsig.initialize().wait()
# Server SDK is now ready
user = StatsigUser("123")
gate = statsig.check_gate(user, "my_gate")
```
{% /codetabs %}
{% /tab %}
{% /tabs %}
================================================================================
# How Evaluation Works
URL: https://docs.statsig.com/sdks/how-evaluation-works
Updated: 2026-07-11
================================================================================
## Why evaluation matters
The essential function of the Statsig SDKs is reliable, consistent, high-performance allocation of users to the correct bucket in your experiment or feature gate. Understanding how evaluation works can help you answer questions like:
* Why do I have to pass every user attribute every time?
* Why do I have to wait for initialization to complete?
* When does Statsig decide each user's bucket?
## How evaluation works
Evaluation in Statsig is deterministic. Given the same user object and the same state of the experiment or feature gate, Statsig always returns the same result, even across different platforms (client or server). The process is:
1. **Salt Creation**: Each experiment or feature gate rule generates a unique salt.
2. **Hashing**: The user identifier (e.g., userId, organizationId) is passed through a SHA256 hashing function, combined with the salt, which produces a large integer.
3. **Bucket Assignment**: The large integer is then subjected to a modulus operation with 10000 (or 1000 for layers), assigning the user to a bucket.
4. **Bucket Determination**: The result defines the specific bucket out of 10000 (or 1000 for layers) where the user is placed.
This process ensures randomized but deterministic bucketing across experiments and feature gates. The unique salt per rule ensures that the same user can be assigned to different buckets in different experiments. If you roll out a feature gate rule to 50%, back to 0%, then back to 50%, the same 50% of users are re-exposed, **as long as you reuse the same rule** and don't create a new one. Go to the [FAQ](/faq/#when-i-change-the-rollout-percentage-of-a-rule-on-a-feature-gate-will-users-who-passed-continue-to-pass) for details.
For more details, go to the [open-source SDK evaluator](https://github.com/statsig-io/node-js-server-sdk/blob/main/src/Evaluator.ts).
For advanced use cases (for example, a series of related experiments that must reuse control and test buckets), Statsig exposes the ability to copy and set the salts used for deterministic hashing. Use this with care. This option is only available to Project Administrators and appears in the Overflow (...) menu in Experiments.
## Evaluation order
When evaluating gates, experiments, and layers, the SDK iterates through a list of rules generated by the server. Rules are evaluated sequentially and the first matching rule determines the result. Overrides always take precedence because they appear first in the rule list.
{% callout type="note" %}
Each step uses the hash-based bucketing described above. Layer allocation and group assignment use different salts, so a user's position in the layer is independent of their group assignment within the experiment.
{% /callout %}
### Experiments
When an experiment is evaluated (you call `getExperiment`), it follows this evaluation order:
1. **ID overrides**: Specific user/unit IDs mapped to a group
2. **Conditional overrides**: Segment or gate-based overrides, evaluated in order
3. **Layer holdouts**: If the experiment is in a layer, layer-level holdout gates are checked
4. **Holdout gates**: Experiment-level holdout gates; users in a holdout receive default values
5. **Experiment exclusion**: Mutual exclusion segments that prevent users from being in multiple experiments
6. **Start status**: If the experiment is not started, users receive default values (with optional non-production environment exceptions)
7. **Layer allocation**: For experiments in a layer, the user's bucket (based on the layer's universe salt) must fall within the experiment's allocated segments. This is checked **before** targeting.
8. **Targeting gate**: Users who fail the targeting gate receive default values. This is checked **after** layer allocation.
9. **Group assignment**: The user's bucket (based on the experiment salt) determines which group they fall into. Groups are cumulative ranges across 1000 buckets.
### Layers
When a layer is evaluated (you call `getLayer`), it follows this evaluation order:
1. **Override rules**: ID overrides from all experiments in the layer
2. **Layer holdout gates**: Holdout gates attached to the layer
3. **Experiment allocation**: Each experiment in the layer has a `configDelegate` rule. The user's bucket determines which experiment they are delegated to.
4. **Delegated experiment evaluation**: Once delegated, the experiment's own evaluation runs (start status, targeting gate, group bucketing as described above)
If no experiment allocation rule matches, the user receives the layer's default values.
### Holdouts
Holdout gates evaluate in this order:
1. **Experiment exclusion**: Exclusion segments (if applicable)
2. **ID overrides**: Specific user/unit IDs
3. **Population targeting gate**: If the holdout has a targeting gate, users who fail it are not held out
4. **Holdout percentage**: The pass percentage on the holdout rule determines the holdout rate
### Gates
When a feature gate is evaluated (you call `checkGate`), it follows this evaluation order:
1. **ID overrides**: Specific user/unit IDs mapped to pass/fail
2. **Conditional overrides**: Segment or gate-based overrides
3. **Holdout rules**: If the gate has holdouts attached
4. **Rules**: The gate's targeting rules, evaluated in the order they appear in the console. Each rule has its own conditions and pass percentage.
## When evaluation happens
On server SDKs, evaluation happens when the gate or experiment is checked. Server SDKs hold the entire ruleset of your project in memory (a JSON representation of each gate or experiment). On client SDKs, Statsig evaluates all gates and experiments server-side when you call `initialize`.
This logic applies to both SDK types. In both cases, the user's assignment bucket isn't sent to Statsig until you call `getExperiment` or `checkGate`.
## What this means
* **Performant evaluation:** No evaluations require a network request. Checks take under 1 ms after initialization.
* **SDKs don't remember user attributes or previous evaluations:** Pass all required user attributes consistently on every call. Statsig returns the same value for the same inputs.
A common assumption is that Statsig maintains a list of all IDs and their assigned groups. Statsig's data pipelines track users exposed to each variant to compute experiment results, but Statsig doesn't cache previous evaluations or maintain distributed evaluation state across client and server SDKs.
* **Server SDKs can handle multiple users:** Because server SDKs hold the ruleset in memory, they can evaluate any user without a network request. Pass a user object into `getExperiment` on server SDKs; on client SDKs, pass the user object into `initialize`.
* **Each user receives the same bucket:** ID-based hashing guarantees consistency. If you make a change in the console that could affect user bucketing on an experiment, Statsig provides a warning.
## Evaluation of null or empty unit IDs
Statsig doesn't apply any filtering or business logic before assigning an individual user ID. Statsig buckets a null or empty unit ID based on the salt.
================================================================================
# SDK Debugging
URL: https://docs.statsig.com/sdks/debugging
Updated: 2026-07-11
================================================================================
## Debugging tools
When a user sees an unexpected value, start with the tooling built into the Statsig console and SDKs.
### Diagnostics & log stream
Every gate, config, experiment, and layer has both a **Setup** and **Diagnostics** tab. The diagnostics view highlights pass/fail rates and bucketing counts so you can spot anomalies over time.
{% figure %}

{% /figure %}
Scroll to the log stream to inspect individual evaluations. Entries arrive within seconds for both production and non-production environments.
{% figure %}

{% /figure %}
{% callout type="note" %}
Enable **Show non-production logs** in the diagnostics view to surface checks coming from test keys and development builds.
{% /callout %}
### Logging levels and expected information
Statsig SDKs emit runtime logs across four verbosity levels:
* `Debug` – deep tracing meant for onboarding or issue triage.
* Missing gate/config warnings when a definition is unavailable.
* Step-by-step messages that follow SDK initialization and evaluations.
* `Info` – healthy lifecycle events for day-to-day operation.
* Initialization summaries with source and SDK version details.
* Notifications when the configuration store is populated.
* `Warning` – recoverable issues that might affect functionality.
* Non-critical errors automatically handled by the SDK.
* gRPC reconnection attempts or similar transient network events.
* `Error` – critical failures that block expected behavior.
* Initialization timeouts or outright failures.
* Fallback notices that indicate gRPC is unavailable or misconfigured.
## Evaluation details
Click any exposure in the log stream to drill into the precise rule, user attributes, evaluation reason, SDK metadata, and server timestamps. That detail is often enough to pinpoint why a user received a specific value.
{% figure %}

{% /figure %}
### Evaluation reasons
Evaluation reasons answer two questions: where the SDK sourced its definitions and why a particular value was returned. Use them to distinguish between healthy results, overrides, and error states.
{% figure %}

{% /figure %}
{% tabs %}
{% tab title="Client SDKs" %}
#### Data source
| Source | Description | Type | Debugging Suggestions |
| --- | --- | --- | --- |
| `Network` | Values fetched during initialization from Statsig servers. | Normal | None |
| `Bootstrap` | Supplied during bootstrap (often from a server SDK). | Normal | None |
| `Prefetch` | Loaded via the `prefetchUsers` API (JS only). | Normal | None |
| `NetworkNotModified` | Network request succeeded but cached values were already current. | Normal | None |
| `Sticky` (legacy) | Persisted from a sticky evaluation. | Normal | None |
| `LocalOverride` (legacy) | Set locally via override APIs. | Normal | None |
| `Cache` | Served from local cache because network values were unavailable. | Warning | Ensure `initialize()` has completed before checks run. |
| `InvalidBootstrap` / `BootstrapPartialUserMatch` | Bootstrap values were generated for a different user profile. | Error | Refer to [Fixing InvalidBootstrap](#invalid-bootstrap). |
| `BootstrapStableIDMismatch` | StableID differed between bootstrap and runtime user. | Error | Refer to [BootstrapStableIDMismatch](#bootstrapstableidmismatch). |
| `Error` | A generic evaluation failure that was logged to Statsig. | Error | Ask for help in [Slack](https://statsig.com/slack). |
| `Error:NoClient` (JS) | No Statsig client was found in context. | Error | Wrap checks in `` or equivalent. |
| `Unrecognized` (legacy) | The definition was missing from the initialize payload. | Error | Confirm the config exists and the correct API key is used. |
| `NoValues` | Initialization ran but failed to retrieve values. | Error | Verify client key and network connectivity. |
| `Loading` | Initialization is still in progress. | Error | Await `initializeAsync()` or guard checks until ready. |
| `Uninitialized` | Initialization never started. | Error | Call `initializeAsync()`/`initializeSync()` explicitly. |
| `UAParserNotLoaded` | UA parsing was disabled while targeting relies on it. | Error | Remove UA-based targeting or re-enable parsing. |
| `CountryLookupNotLoaded` | Country lookups were disabled while targeting relies on them. | Error | Avoid IP-based targeting or re-enable lookups. |
#### Reason (new SDKs only)
| Reason | Description | Type | Debugging Suggestions |
| --- | --- | --- | --- |
| `Recognized` | The definition was present and matched the current values. | Normal | None |
| `Sticky` | Result persisted because `keepDeviceValue` was set. | Normal | None |
| `LocalOverride` | Value came from a developer override. | Normal | None |
| `Unrecognized` | Definition missing from the payload. | Error | Confirm targeting and [Target Apps](/sdks/target-apps). |
| `Filtered` | Definition filtered from `/initialize` because the default value was `false`. | Error | Check [client bootstrapping](/client/concepts/initialize#bootstrapping-overview) or targeting.
{% /tab %}
{% tab title="Server SDKs" %}
#### Data source
| Source | Description | Type | Debugging Suggestions |
| --- | --- | --- | --- |
| `Network` | Configurations loaded from Statsig servers at initialization. | Normal | None |
| `Bootstrap` | Server SDK bootstrapped with precomputed values. | Normal | None |
| `DataAdapter` | Values retrieved from a data adapter or external store. | Normal | Review your [data adapter setup](/server/concepts/data_store#dataadapter-or-datastore). |
| `LocalOverride` (legacy) | Value supplied via server-side override APIs. | Normal | None |
| `StatsigNetwork` | Proxy/streaming fell back to Statsig APIs directly. | Fallback | Revisit your [proxy configuration](/server/concepts/forward_proxy/). |
| `Uninitialized` | SDK has not completed initialization. | Error | Ensure initialization succeeds before evaluations. |
| `Unrecognized` (legacy) | Definition missing from the cached payload. | Error | Confirm configuration exists and the API key is correct. |
#### Reason (new SDKs only)
| Reason | Description | Type | Debugging Suggestions |
| --- | --- | --- | --- |
| `LocalOverride` | Result supplied by a developer override. | Normal | None |
| *None* | Successful evaluation with the expected payload. | Normal | None |
| `Unrecognized` | Definition missing from the payload. | Error | Confirm configuration exists and the API key is correct. |
| `Unsupported` | SDK lacks support for a condition/operator. | Error | Upgrade to the latest SDK version. |
| `Error` | Generic evaluation failure captured by the SDK. | Error | Ask for help in [Slack](https://statsig.com/slack). |
{% /tab %}
{% /tabs %}
### Evaluation times
Evaluation timestamps reveal whether an SDK is serving fresh definitions. An up-to-date LCUT (Last Config Updated Time) indicates the SDK has the latest changes. If LCUT lags behind, users may be seeing stale values. This can happen because a browser tab remained open or because a server integration cannot sync.
{% tabs %}
{% tab title="Client SDKs" %}
| Time Field | Description | SDKs |
| --- | --- | --- |
| `LCUT` | Time of the most recent config change reflected in the SDK. | JavaScript (incl. React & RN), iOS, Dart |
| `receivedAt` | Timestamp when the client obtained the current values. | JavaScript (incl. React & RN), iOS, Dart |
{% /tab %}
{% tab title="Server SDKs" %}
| Time Field | Description | SDKs |
| --- | --- | --- |
| `initTime` | LCUT captured when the server SDK initialized. | All server SDKs except Rust |
| `configSyncTime` | Latest LCUT received from the network or data adapter. | All server SDKs except Rust |
| `serverTime` | Current server clock time when the evaluation was made. | All server SDKs except Rust |
{% /tab %}
{% /tabs %}
### Mocking Statsig / local mode
Use the following tools to validate code paths without making network requests:
* **Local mode:** Set `localMode` to `true` so the SDK skips network calls and returns default values. This is useful for tests and offline environments.
* **Override APIs:** Call `overrideGate` and `overrideConfig` to force specific values for an individual user or globally.
Use both techniques together to exercise each branch of your application before shipping.
### Client SDK debugger
Inspect the exact values a client SDK is using by opening the Client SDK Debugger. It exposes the active user object and every gate/config tied to that client.
* **JavaScript / React:** Use the [Chrome extension](https://github.com/statsig-io/statsig-sdk-debugger-chrome-extension).
* **iOS:** Call `Statsig.openDebugView()` in [v1.26.0](https://github.com/statsig-io/statsig-kit/releases/tag/v1.26.0) or later.
* **Android:** Call `Statsig.openDebugView()` in [v4.29.0](https://github.com/statsig-io/android-sdk/releases/tag/4.29.0) or later.
{% callout type="note" %}
Accounts that sign in to the Statsig console through Google SSO aren't supported by the Chrome extension.
{% /callout %}
| Landing | Gates List | Gate Details | Experiment Details |
| --- | --- | --- | --- |
|  |  |  |  |
## FAQ
For SDK-specific edge cases, check each SDK’s FAQ or reach out to the Statsig team in the [Statsig Slack community](https://statsig.com/slack).
### Invalid bootstrap
`InvalidBootstrap` occurs when a client SDK is bootstrapped with values generated for a different user profile. The bootstrap user must exactly match the runtime user object.
```js
// Server side
const userA = { userID: 'user-a' };
const bootstrapValues = Statsig.getClientInitializeResponse(userA);
// Client side
const bootstrapValues = await fetchStatsigValuesFromMyServers();
const userB = { userID: 'user-b' }; // <-- Different from userA
await Statsig.initialize('client-key', userB, { initializeValues: bootstrapValues });
```
Even subtle differences count as a mismatch. Adding `customIDs` or other attributes results in a distinct user object.
```js
const userA = { userID: 'user-a' };
const userAExt = { userID: 'user-a', customIDs: { employeeID: 'employee-a' } };
```
### BootstrapStableIDMismatch
`BootstrapStableIDMismatch` is similar to `InvalidBootstrap` but focuses on `stableID`. Client SDKs generate a stable ID automatically when one is not provided, so mixing empty user objects between server and client code can cause drift.
```js
// Server side
const userA = {};
const bootstrapValues = Statsig.getClientInitializeResponse(userA);
// Client side
const bootstrapValues = await fetchStatsigValuesFromMyServers();
const userB = { stableID: '12345' }; // <-- Server user lacked a stableID
await Statsig.initialize('client-key', userB, { initializeValues: bootstrapValues });
```
Even if both sides start with `{}`, the client-generated stable ID may not match the server’s stable ID, which triggers the same mismatch warning.
```js
const userC = {}; // Client SDK auto-generates a stableID
await Statsig.initialize('client-key', userC, { initializeValues: bootstrapValues });
```
### Environments
SDKs inherit their environment from initialization options. If you don't provide an environment, the SDK defaults to production. To verify which environment a user evaluated under, open the diagnostics log stream and inspect the `statsigEnvironment` property on the exposure entry.
### Maximizing event throughput
{% callout type="info" %}
Python SDK v0.45.0+ introduces tunables that help handle high event volume without drops.
{% /callout %}
The SDK batches events and retries failures in the background. When throughput spikes, adjust these options to reduce dropped events:
* **`eventQueueSize`** – Number of events flushed per batch. Larger batches increase throughput but use more memory. Keep the value under approximately 3000 to avoid oversized payloads.
* **`retryQueueSize`** – Number of batches kept for retrying. The default is 10. Raise this value to retain more data at the cost of higher memory usage.
Tune both settings to match your traffic profile.
================================================================================
# Client vs Server SDKs
URL: https://docs.statsig.com/sdks/client-vs-server
Updated: 2026-07-11
================================================================================
Statsig offers client and server SDKs to enable experimentation and feature management across different parts of your application. This page outlines when to choose each.
## Overview
**Client SDKs** run in code that executes on end-user devices, such as a website, mobile app, video game, or smart TV app.
**Server SDKs** run on your servers (typically in the cloud), such as a web server or API server.
Many customers deploy both Server and Client SDKs, which lets them gate features on both the client and server side. While this is common, you can get value from Statsig by starting with one SDK type.
## Usage
Client and Server SDKs follow a similar setup pattern:
1. **Initialize:** Set up the SDK and download the latest values.
2. **Check an experiment/gate:** Reference those values to assign an experiment or flag a feature.
3. **Log custom events:** Log important app metrics to power your analyses.
{% callout type="note" %}
After initialization, both Client and Server SDKs evaluate experiments/gates *without a network request*, and typically in less than 1ms. Checks in the Statsig SDKs are designed to be very efficient.
{% /callout %}
## Conceptual differences
* Data Privacy: The Server SDK runs in a secure, multi-user environment, so Server SDKs have access to the full ruleset describing each experiment and gate. Client SDKs fetch only the value for a single user, avoiding exposure of your configuration definitions.
||Server| Client |
|--|--|--|
|Privacy | Your servers operate in a secure, multi-user environment, so Server SDKs have access to the definition of all configurations in your project|Statsig precomputes all evaluations on its servers and sends results to your client applications. Names are obfuscated, but a savvy user may be able to glean information from the raw response|
|Evaluation Performance | The SDK evaluates in real time, without a network request. Very complex configurations can take longer to compute, but in practice, this is rarely an issue.|As Statsig precomputes all evaluation, gate and experiment checks are effectively a dictionary lookup with some computation used for creating and flushing exposure events|
|Initialization Performance | The SDK makes an upfront request for configuration files, then continually polls for any changes to your configurations, updating its internal state when it detects a change| Client SDKs download configurations when you initialize, before which, the SDK may not have usable values. Values don't update mid-session unless you explicitly call updateUser. Additional options are available for performant initialization; refer to [Initializing](/client/concepts/initialize)|
|Users|Server SDKs are designed to run against multiple users, and all SDK methods require a user object for evaluation/logging |Client SDKs are designed to run with one user at a time. The SDK loads all evaluations once during initialization, and every event logged uses that user object|
|Infrastructure | Server SDKs require you to host your own backend services| Client SDKs run entirely on the client and use Statsig's servers|
## Usage differences
||Server| Client |
|--|--|--|
|Initializing| Requires only a secret key, downloads the entire ruleset and syncs it in the background | Requires a client key and a user object. Before/during initialization, the SDK attempts to fall back to cached values.|
|Checking an Experiment| Requires a user object, which the SDK evaluates locally (without a network request) against a ruleset in memory | Doesn't require a user object, uses a dictionary lookup for values fetched during initialize() |
|User Identifiers| Pass any and all useful user identifiers | Pass any useful identifiers, the SDK also generates a "StableID", Statsig's anonymous ID you can use to experiment on a user per-device|
|Logging Events| Requires a user object | Doesn't require a user object. There is some risk of adblocking log events on client SDKs, which you can minimize by setting up a [Custom Proxy](/infrastructure/api_proxy/custom_proxy)|
|Flushing Events| Batched and flushed by the SDK every 60 seconds | Batched and flushed by the SDK every 10 seconds|
|Updating Configurations| Poll Statsig servers for updates every 10 seconds by default (configurable), Streaming possible with some Server SDKs and the [Statsig Forward Proxy](/server/concepts/forward_proxy) | Configuration persists until next `initialize` or `updateUser` call; call `initialize` at the start of each user session|
## Initialize and update logic
**Client SDKs:**
* Configuration persists until next `initialize` or `updateUser` call
* Call `initialize` at the start of each user session
{% figure %}

{% /figure %}
**Server SDKs:**
* Poll Statsig servers for updates every 10 seconds by default (configurable)
* Some SDKs support grpc streaming updates by connecting to the [Statsig Forward Proxy](/server/concepts/forward_proxy)
{% figure %}

{% /figure %}
## Available SDKs
### Client SDKs
* [Javascript](/client/javascript-sdk)
* [React](/client/React)
* [React Native](/client/ReactNative)
* [Expo](/client/Expo)
* [iOS](/client/iosClientSDK)
* [Android](/client/Android)
* [.Net](/client/DotNet)
* [Unity](/client/Unity)
* [Roku](/client/Roku)
* [C++](/client/CPP)
* [Dart/flutter](/client/Dart)
### Server SDKs
* [Node.js](/server/nodejsServerSDK)
* [Java](/server/java)
* [Python](/server/pythonSDK)
* [Go](/server/go)
* [Ruby](/server/ruby)
* [.NET Core](/server-core/dotnet-core)
* [C++](/server-core/cpp-core)
* [PHP](/server/php)
* [Rust](/server/rust)
For more information on each SDK, refer to their respective documentation pages.
Got questions? Join the Statsig engineering and product team on the [Statsig Slack channel](https://statsig.com/slack).
================================================================================
# Data Privacy for Mobile
URL: https://docs.statsig.com/compliance/data_privacy_for_mobile
Updated: 2026-07-11
================================================================================
## General
### What data does Statsig collect from users of my app?
Statsig collects only the data that you configure to be sent to Statsig. This is typically the occurrence of feature flag evaluations (Feature Gates), experiment exposures, and custom events you log with the SDK.
### Does that data include any personally identifiable information (PII)?
By default, Statsig uses randomly generated IDs as described in this article. You can also augment data sent to Statsig with additional context and metadata, including user names, email addresses, or custom attributes. This data, alone or in combination with other data, may constitute PII if it identifies an individual directly or indirectly.
### Does Statsig use the device ID to identify a user?
No. Statsig on Mobile doesn't use device IDs such as `Secure.ANDROID_ID` or `advertisingIdentifier` on iOS. Instead, Statsig uses a StableID, which is randomly generated per device, per app, and per installation, and can't be used to identify a single device across installations. A new StableID is generated when an application is reinstalled. These IDs provide approximate statistical information as part of the application monitoring service, for example, for Crash Free Session and User Rates, and to indicate the impact of issues based on number of events versus affected users.
### What does Statsig do with the data it collects?
Statsig processes the data you send to it to provide feature flag management, experimentation, and analytics services.
## Apple App Store
### Do I need to disclose the use of Statsig in App Store Connect on the Apple App Store?
Yes, Statsig is a third-party partner whose code (SDKs) you integrate in your app that collects data from users of your app.
### What do I need to disclose to Apple?
Disclose all types of data you are collecting through your app, including data you are sending to Statsig. This may include "Contact Info" or "Identifiers" if you provide those in the `StatsigUser` object. Include any other categories of data you have configured the SDKs to send to Statsig.
### How does Statsig use my data?
The standard data use cases for Statsig are "Analytics" and "App Functionality". Disclose to Apple any other ways in which you or your app use the data you collect.
### Does Statsig use my data to track users?
Statsig doesn't use your data to track users. However, if you or your other third-party partners are tracking users, you still need to disclose this to Apple.
### Does Statsig use the Advertising Identifier (IDFA)?
No. Statsig doesn't require IDFA.
## Google Play
### Does Statsig collect any PII from children?
If your app targets children and you configure Statsig to collect PII, Statsig collects the elements you have designated. You remain responsible for obtaining appropriate parental consents for the PII you collect from users and send to Statsig. You also remain responsible for declaring your app's target age group to Google Play.
### Do Statsig SDKs cause my app to contain ads?
No. Statsig doesn't cause your app to contain any ads.
### What do I need to disclose to Google Play?
Disclose in your app privacy policy all types of data you are collecting through your app, including data you are sending to Statsig.
## Privacy Controls
### What device and user metadata does Statsig automatically collect?
The Statsig SDKs automatically collect the following metadata for targeting and analytics purposes:
**iOS SDK collects:**
* appIdentifier: Your app's bundle identifier
* appVersion: Your app's version
* deviceModel: The device model (e.g., iPhone14,3)
* deviceOS: The operating system (iOS)
* language: The user's preferred language
* locale: The user's locale identifier
* sdkType: The type of SDK (ios-client)
* sdkVersion: The version of the Statsig SDK
* sessionID: A randomly generated UUID for the current session
* stableID: A persistent device identifier (see below)
* systemVersion: The iOS version
* systemName: The system name (iOS)
**Android SDK collects:**
* appIdentifier: Your app's package name
* appVersion: Your app's version name
* deviceModel: The device model (e.g., Pixel 7)
* deviceOS: The operating system (Android)
* locale: The user's locale
* language: The user's language
* sdkType: The type of SDK (android-client)
* sdkVersion: The version of the Statsig SDK
* sessionID: A randomly generated UUID for the current session
* stableID: A persistent device identifier (see below)
* systemVersion: The Android API level
* systemName: The system name (Android)
### How can I limit the metadata collected by Statsig?
Both iOS and Android SDKs provide the `optOutNonSdkMetadata` option to limit the collection of device-specific information:
**iOS SDK:**
```swift
let options = StatsigOptions()
options.optOutNonSdkMetadata = true
Statsig.start(sdkKey: "client-sdk-key", options: options)
```
**Android SDK:**
```kotlin
val options = StatsigOptions(optOutNonSdkMetadata = true)
```
When `optOutNonSdkMetadata` is enabled, only the following core SDK metadata is included:
* sdkType: The type of SDK
* sdkVersion: The version of the Statsig SDK
* sessionID: A randomly generated UUID for the current session
* stableID: A persistent device identifier
All device-specific information (appIdentifier, appVersion, deviceModel, deviceOS, locale, language, systemVersion, systemName) is excluded from logs and targeting.
### How does StableID work?
The StableID is a persistent identifier that Statsig uses to provide consistent user experiences and analytics:
**iOS Implementation:**
* The StableID is stored in UserDefaults with the key "com.Statsig.InternalStore.stableIDKey"
* When first generated, the StableID is created as a random UUID
* The StableID persists across app launches but is regenerated when the app is reinstalled
* The StableID can be overridden through the `StatsigOptions.overrideStableID` parameter
**Android Implementation:**
* The StableID is stored in SharedPreferences
* When first generated, the StableID is created as a random UUID
* The StableID persists across app launches but is regenerated when the app is reinstalled
* The StableID can be overridden through the `StatsigOptions.overrideStableID` parameter
The StableID isn't shared across different apps or websites and can't be used to track users across different applications or platforms.
### How can I prevent sending sensitive user data to Statsig?
Use the `privateAttributes` field for sensitive data you want to use for targeting but not log:
**iOS SDK:**
```swift
let user = StatsigUser(
userID: "user-123",
email: nil, // Not included at top level to keep private
privateAttributes: ["email": "user@example.com"] // Used for evaluation but not logged
)
```
**Android SDK:**
```kotlin
val user = StatsigUser("user-123")
user.privateAttributes = mapOf("email" to "user@example.com")
```
The SDK sends these attributes to Statsig servers during initialization for feature flag and experiment evaluation, but Statsig removes them before sending any event logs to Statsig servers. Use the attributes for targeting users with specific features or experiments, but they don't appear in your analytics data.
For more comprehensive privacy controls, use [Client Bootstrapping](/client/concepts/initialize#bootstrapping-overview) to generate all assignments locally on your server, which eliminates the need to send any user attributes from the client device to Statsig.
================================================================================
# JavaScript Client SDK (Web)
URL: https://docs.statsig.com/client/javascript-sdk
Updated: 2026-07-11
================================================================================
## Set Up the SDK
{% steps %}
{% step title="Install the SDK" %}
To install the Statsig Web SDK, add the package using your preferred package manager. Include optional packages if you plan to enable Session Replay or Auto Capture.
{% tabs %}
{% tab title="npm" %}
```bash
npm install @statsig/js-client @statsig/session-replay @statsig/web-analytics
```
{% /tab %}
{% tab title="yarn" %}
```bash
yarn add @statsig/js-client @statsig/session-replay @statsig/web-analytics
```
{% /tab %}
{% /tabs %}
{% callout type="info" %}
If you don't need Session Replay or Auto Capture, omit the `@statsig/session-replay` and `@statsig/web-analytics` packages.
{% /callout %}
After installation, configure the SDK in your app entry point before rendering your UI.
{% /step %}
{% step title="Initialize the SDK" %}
Next, initialize the SDK with a client SDK key from the ["API Keys" tab on the Statsig console](https://console.statsig.com/api_keys). These keys are safe to embed in a client application.
Along with the key, pass in a [User Object](#statsig-user) with the attributes you'd like to target later in a gate or experiment.
```tsx
import { StatsigClient } from '@statsig/js-client';
const client = new StatsigClient(
'client-xyz',
{ userID: 'a-user' },
{
environment: { tier: 'development' },
},
);
await client.initializeAsync();
```
Use `initializeAsync` when you need to await the latest values. For a non-blocking approach, you can call `initializeAsync()` without awaiting and rely on cached values until the promise resolves.
{% /step %}
{% /steps %}
## Use the SDK
### Checking a Feature Flag/Gate
Now that your SDK is initialized, check a [**Feature Gate**](/feature-flags/overview). Feature Gates create logic branches in code that can be rolled out to different users from the Statsig Console. Gates are always **CLOSED** or **OFF** (think `return false;`) by default.
```tsx
if (client.checkGate('new_homepage_design')) {
// Gate is on, show new experience
} else {
// Gate is off, render the default experience
}
```
### Reading a Dynamic Config
Feature Gates work well for simple on/off switches with optional advanced user targeting. To send different values (strings, numbers, and similar types) to your clients based on specific user attributes such as country, use **Dynamic Configs**. The API is similar to Feature Gates but returns a full JSON object you can configure on the server and fetch typed parameters from. For example:
```tsx
const config = client.getDynamicConfig('awesome_product_details');
const itemName = config.get('product_name', 'Some Fallback');
const price = config.value.price ?? 10.0;
if (config.value.is_discount_enabled === true) {
// apply discount logic
}
```
### Getting a Layer/Experiment
Use **Layers/Experiments** to run A/B/n experiments. Statsig offers two APIs, but recommends [layers](/experiments/layers-overview) to enable quicker iterations with parameter reuse.
```tsx
// Reading values via getLayer
const layer = client.getLayer('user_promo_experiments');
const promoTitle = layer.get('title', 'Welcome to Statsig!');
const discount = layer.get('discount', 0.1);
```
```tsx
// Reading values via getExperiment
const titleExperiment = client.getExperiment('new_user_promo_title');
const priceExperiment = client.getExperiment('new_user_promo_price');
const experimentTitle = titleExperiment.value.title ?? 'Welcome to Statsig!';
const experimentDiscount = priceExperiment.value.discount ?? 0.1;
```
### Logging an Event
After you set up a Feature Gate or an Experiment, you can track custom events to measure how your new features or experiment groups affect those events. Call the Log Event API for the event, and optionally provide a value and/or a metadata object to log together with the event:
```tsx
client.logEvent('my_simple_event');
client.logEvent({
eventName: 'add_to_cart',
value: 'SKU_12345',
metadata: {
price: '9.99',
item_name: 'diet_coke_48_pack',
},
});
```
### Flushing Logged Events
`flush()` sends queued events immediately. Use `shutdown()` when your app is exiting.
```tsx
await client.flush();
```
### Typed Getters
`Layer`, `Experiment`, and `DynamicConfig` objects support a typed `get` method. Using a fallback that matches the expected type helps avoid returning unintended values.
```tsx
// config value: { "my_value": 1 }
const dynamicConfig = client.getDynamicConfig('a_config');
const fallbackString = dynamicConfig.get('my_value', 'fallback'); // returns 'fallback'
const fallbackNumber = dynamicConfig.get('my_value', 0); // returns 1
const rawValue = dynamicConfig.get('my_value'); // returns 1
```
Passing a fallback of the wrong type returns that fallback. When type safety is not needed, omit the fallback to receive the raw value.
### Evaluation Details
Each gate, config, experiment, and layer exposes `details` describing how the value was resolved.
* `reason` explains the source (e.g., `Network:Recognized`, `Cache:Unrecognized`).
* `lcut` is the last time any configuration changed in your project.
* `receivedAt` marks when this response was received, useful for judging cache staleness.
```tsx
const gate = client.getFeatureGate('a_gate');
console.log(gate.details);
// { reason: 'Cache:Recognized', lcut: 1713837126636, receivedAt: 1713838137598 }
const config = client.getDynamicConfig('a_config');
console.log(config.details);
// { reason: 'Cache:Unrecognized', lcut: 1713837126636, receivedAt: 1713838137598 }
```
Go to [`/sdk/debugging`](/sdks/debugging) for the full list of `reason` values.
### Sample Projects
Explore end-to-end examples in the [`js-client-monorepo` samples folder](https://github.com/statsig-io/js-client-monorepo/tree/main/samples) for React, Next.js, precomputed clients, and more.
## Parameter Stores
Parameter Stores hold a set of parameters for your mobile app. These parameters can be remapped dynamically from a static value to a Statsig entity (Feature Gates, Experiments, and Layers), so you can decouple your code from the configuration in Statsig. Go to [Parameter Stores](/client/concepts/parameter-stores) to learn more.
```tsx
const homepageStore = client.getParameterStore('homepage');
const title = homepageStore.get('title', 'Welcome');
const showUpsell = homepageStore.get('upsell_upgrade_now', false);
```
## Statsig User
Provide a StatsigUser object to check or get your configurations. Pass as much information as possible to take advantage of advanced gate and config conditions.
The `userID` field is usually required to provide a consistent experience for a given user. (Refer to [logged-out experiments](/guides/first-device-level-experiment) to understand how to correctly run experiments for logged-out users.)
Besides `userID`, the `email`, `ip`, `userAgent`, `country`, `locale`, and `appVersion` fields are also available as top-level fields on StatsigUser. You can also pass any key-value pairs in an object/dictionary to the `custom` field to create targeting based on them.
After the user logs in or their attributes change, call `updateUser` with the updated `userID` and/or any other updated user attributes.
### Updating Users
Call `updateUserAsync` when the signed-in user changes to fetch fresh values for that identity.
```tsx
const user = { userID: 'a-user' };
await client.updateUserAsync(user);
```
For advanced flows such as bootstrapping or prefetching users, go to [Using EvaluationsDataAdapter](/client/javascript-mono/UsingEvaluationsDataAdapter).
### Prefetching Users
Use `prefetchData` to prepare values for another user so you can switch synchronously later.
```tsx
const nextUser = { userID: 'my-other-user' };
await client.dataAdapter.prefetchData(nextUser);
// Optionally handle failures without blocking the UI
client.dataAdapter.prefetchData(nextUser).catch((err) => {
console.warn('Failed to prefetch', err);
});
client.updateUserSync(nextUser);
const gate = client.getFeatureGate('a_gate');
console.log(gate.value, gate.details.reason); // true, 'Prefetch:Recognized'
```
## Statsig Options
{% parameter name="loggingEnabled" type="LoggingEnabledOption" %}
Controls logging behavior.
* `browser-only` (default): log events from browser environments.
* `disabled`: never send events.
* `always`: log in every environment, including non-browser contexts.
{% /parameter %}
{% parameter name="disableLogging" type="boolean" %}
Use `loggingEnabled: 'disabled'` instead.
{% /parameter %}
{% parameter name="disableStableID" type="boolean" %}
Skip generating a device-level Stable ID.
{% /parameter %}
{% parameter name="disableEvaluationMemoization" type="boolean" %}
Recompute every evaluation instead of using the memoized result.
{% /parameter %}
{% parameter name="initialSessionID" type="string" %}
Override the generated session ID.
{% /parameter %}
{% parameter name="enableCookies" type="boolean" %}
Persist Stable ID in cookies for cross-domain tracking.
{% /parameter %}
{% parameter name="disableStorage" type="boolean" %}
Prevent any local storage writes (disables caching).
{% /parameter %}
{% parameter name="networkConfig" type="NetworkConfig" %}
Override network endpoints per request type.
{% /parameter %}
{% accordion title="Network Config Options" %}
{% parameter name="api" type="string" %}
Base URL for all requests. The SDK appends endpoint paths like `/initialize` and `/rgstr`; append `/v1` when your proxy expects it.
{% /parameter %}
{% parameter name="initializeUrl" type="string" %}
Endpoint for initialization requests only. Takes precedence over `api` for `/initialize`.
{% /parameter %}
{% parameter name="initializeFallbackUrls" type="string[]" %}
Fallback endpoints for initialization requests only. This doesn't create a generic fallback for `api`.
{% /parameter %}
{% parameter name="logEventUrl" type="string" %}
Endpoint for event uploads.
{% /parameter %}
{% parameter name="logEventFallbackUrls" type="string[]" %}
Fallback endpoints for event uploads only. This doesn't create a generic fallback for `api`.
{% /parameter %}
{% parameter name="networkTimeoutMs" type="number" %}
Request timeout in milliseconds.
{% /parameter %}
{% parameter name="preventAllNetworkTraffic" type="boolean" %}
Disable all outbound requests; combine with `loggingEnabled: 'disabled'` to silence log warnings.
{% /parameter %}
{% parameter name="networkOverrideFunc" type="function" %}
Provide custom transport (e.g., Axios).
{% /parameter %}
{% /accordion %}
{% parameter name="environment" type="StatsigEnvironment" %}
Set environment-wide defaults (for example `{ tier: 'staging' }`).
{% /parameter %}
{% parameter name="logLevel" type="LogLevel" %}
Console verbosity.
{% /parameter %}
{% parameter name="loggingBufferMaxSize" type="number" %}
Max events per log batch.
{% /parameter %}
{% parameter name="loggingIntervalMs" type="number" %}
Interval between automatic flushes.
{% /parameter %}
{% parameter name="overrideAdapter" type="OverrideAdapter" %}
Modify evaluations before returning them.
{% /parameter %}
{% parameter name="includeCurrentPageUrlWithEvents" type="boolean" %}
Attach the current page URL to logged events.
{% /parameter %}
{% parameter name="disableStatsigEncoding" type="boolean" %}
Send requests without Statsig-specific encoding.
{% /parameter %}
{% parameter name="logEventCompressionMode" type="LogEventCompressionMode" %}
Control compression for batched events.
{% /parameter %}
{% parameter name="disableCompression" type="boolean" %}
Use `logEventCompressionMode` instead.
{% /parameter %}
{% parameter name="dataAdapter" type="EvaluationsDataAdapter" %}
Provide a custom data adapter to control caching/fetching.
{% /parameter %}
{% parameter name="customUserCacheKeyFunc" type="CustomCacheKeyGenerator" %}
Override cache key generation for stored evaluations.
{% /parameter %}
## Manual Exposures
{% callout type="warning" %}
Manual logging is error-prone and can often introduce issues like uneven exposures, which compromise experiment results.
{% /callout %}
You can query your gates/experiments without triggering an exposure, and manually log the exposures later:
{% tabs %}
{% tab title="Feature Gates" %}
```tsx
const result = client.checkGate('a_gate_name', { disableExposureLog: true });
// ...
client.checkGate('a_gate_name'); // later, when ready to log the exposure
```
{% /tab %}
{% tab title="Dynamic Configs" %}
```tsx
const config = client.getConfig('a_dynamic_config_name', { disableExposureLog: true });
client.getConfig('a_dynamic_config_name');
```
{% /tab %}
{% tab title="Experiments" %}
```tsx
const experiment = client.getExperiment('an_experiment_name', { disableExposureLog: true });
client.getExperiment('an_experiment_name');
```
{% /tab %}
{% tab title="Layers" %}
```tsx
const layer = client.getLayer('a_layer_name', { disableExposureLog: true });
const value = layer.get('param_name', 'fallback');
// When ready to log
const exposure = client.getLayer('a_layer_name');
exposure.get('param_name', 'fallback');
```
{% /tab %}
{% /tabs %}
## Session Replay
Install `@statsig/session-replay` and register the plugin to record user sessions.
```tsx
import { StatsigProvider } from '@statsig/react-bindings';
import { StatsigSessionReplayPlugin } from '@statsig/session-replay';
Loading...}
options={{ plugins: [new StatsigSessionReplayPlugin()] }}
>
;
```
## Web Analytics / Auto Capture
By including the [`@statsig/web-analytics`](https://www.npmjs.com/package/@statsig/web-analytics) package in your project, you can automatically capture common web events like clicks and page views.
For more information on filtering events, enabling console log capture, and other configuration options available in web analytics, refer to the [Web Analytics Configuration](/webanalytics/overview#event-filtering-and-console-configuration) documentation.
```tsx
import { StatsigProvider } from '@statsig/react-bindings';
import { StatsigAutoCapturePlugin } from '@statsig/web-analytics';
Loading...}
options={{ plugins: [new StatsigAutoCapturePlugin()] }}
>
;
```
## Content Security Policy
Add Statsig endpoints to your CSP `connect-src` directive when running the web SDK.
```js
const cspConfig = {
directives: {
'connect-src': [
'api.statsig.com',
'featuregates.org',
'statsigapi.net',
'events.statsigapi.net',
'api.statsigcdn.com',
'featureassets.org',
'assetsconfigcdn.org',
'prodregistryv2.org',
'cloudflare-dns.com',
'beyondwickedmapping.org',
],
},
};
```
{% callout type="info" %}
Statsig occasionally updates its network domains. Verify the latest list in [Statsig Domains](/infrastructure/statsig_domains).
{% /callout %}
## Lifecycle & Advanced Usage
## Shutting Statsig Down
To save users' data and battery usage and prevent logged events from being dropped, the SDK keeps event logs in client cache and flushes them periodically. Because of this, some events may not have been sent when your app shuts down.
To ensure all logged events are flushed or saved locally, call shutdown when your app is closing.
```tsx
await client.shutdown();
```
## Stable ID
Stable ID provides a consistent device identifier. It lets you run [logged-out experiments](/guides/first-device-level-experiment) and target gates at the device level.
### How Stable ID Works
* On first initialization the SDK generates a Stable ID and stores it in `localStorage` under `statsig.stable_id.`.
* Subsequent sessions reuse the stored value. Each client SDK key has its own Stable ID entry.
* Local storage is scoped per domain, so cross-domain usage requires sharing the value manually (see below).
### Reading the Stable ID
{% tabs %}
{% tab title="JavaScript" %}
```tsx
const context = client.getContext();
console.log('Statsig StableID:', context.stableID);
```
{% /tab %}
{% tab title="React" %}
```tsx
import { useStatsigClient } from '@statsig/react-bindings';
function MyComponent() {
const { client } = useStatsigClient();
const context = client.getContext();
return
{context.stableID}
;
}
```
{% /tab %}
{% /tabs %}
### Overriding the Stable ID
Provide a custom Stable ID through `StatsigUser.customIDs.stableID` if you already manage a durable device identifier.
{% tabs %}
{% tab title="JavaScript" %}
```tsx
import { StatsigClient, StatsigUser } from '@statsig/js-client';
const userWithStableID: StatsigUser = {
customIDs: {
stableID: 'my-custom-stable-id',
},
};
const client = new StatsigClient('client-xyz', userWithStableID);
await client.updateUserAsync(userWithStableID);
```
{% /tab %}
{% tab title="React" %}
```tsx
import { StatsigProvider, useStatsigClient } from '@statsig/react-bindings';
function App() {
return (
Your App
);
}
function MyComponent() {
const { client } = useStatsigClient();
useEffect(() => {
client.updateUserAsync({
customIDs: { stableID: 'my-custom-stable-id' },
});
}, [client]);
}
```
{% /tab %}
{% /tabs %}
{% callout type="note" %}
When you override the Stable ID, Statsig persists it to local storage, so subsequent sessions reuse your custom value.
{% /callout %}
### Sharing Stable ID Across Subdomains
Add this helper script before initializing the SDK and then copy the stored value onto your user object.
```html
```
_Use this script at your discretion and test thoroughly._
### Aligning Stable ID Between Client and Server
To share Stable ID with a backend Statsig SDK, send the value with requests and persist it server-side when missing. The server can bootstrap the client with the same Stable ID.
```tsx
// Server: ensure Stable ID exists, then return initialize response for the client
const values = Statsig.getClientInitializeResponse(user, YOUR_CLIENT_KEY, {
hash: 'djb2',
});
// Client: apply the server-provided values and initialize synchronously
const { values, user: verifiedUser } = await fetch('/init-statsig-client', {
method: 'POST',
body: loadUserData(),
}).then((res) => res.json());
const myClient = new StatsigClient(YOUR_CLIENT_KEY, verifiedUser);
myClient.dataAdapter.setData(values);
myClient.initializeSync();
```
## Using multiple instances of the SDK
The examples above use the SDK's singleton. Statsig also supports creating multiple instances of the SDK. The `Statsig` singleton wraps a single instance of the SDK (typically called a `StatsigClient`) that you can instantiate directly.
{% callout type="note" %}
Use a different SDK key for each SDK instance. Various functionality of the Statsig client is keyed on the SDK key being used. Using the same key causes collisions.
{% /callout %}
All top-level static methods from the singleton carry over as instance methods. To create an instance of the Statsig SDK:
```tsx
import { StatsigClient } from '@statsig/js-client';
const mainClient = new StatsigClient('client-xyz', { userID: 'a-user' });
const secondaryClient = new StatsigClient('client-abc', { userID: 'another-user' });
await Promise.all([
mainClient.initializeAsync(),
secondaryClient.initializeAsync(),
]);
if (mainClient.checkGate('a_gate')) {
// ...
}
if (secondaryClient.checkGate('some_other_gate')) {
// ...
}
```
## Override Adapter
Use the `LocalOverrideAdapter` to define local overrides for gates, configs, experiments, or layers.
```tsx
import { LocalOverrideAdapter } from '@statsig/js-local-overrides';
import { StatsigClient, LogLevel } from '@statsig/js-client';
const overrideAdapter = new LocalOverrideAdapter();
overrideAdapter.overrideGate('gate_a', false);
overrideAdapter.overrideGate('gate_b', true);
const client = new StatsigClient('client-xyz', { userID: 'a-user' }, {
logLevel: LogLevel.Debug,
overrideAdapter,
});
```
### Persisting Overrides
Pass your client SDK key to the adapter to persist overrides between sessions when using multi-instance setups.
```tsx
const overrideAdapter = new LocalOverrideAdapter('client-xyz');
```
## Using Persistent Evaluations
Persist experiment assignments so users keep the same variant even if targeting rules change.
```tsx
import { StatsigClient } from '@statsig/js-client';
import { UserPersistentOverrideAdapter } from '@statsig/js-user-persisted-storage';
class LocalStorageUserPersistedStorage {
load(key: string) {
return JSON.parse(localStorage.getItem(key) ?? '{}');
}
save(key: string, experiment: string, data: string) {
const values = JSON.parse(localStorage.getItem(key) ?? '{}');
values[experiment] = JSON.parse(data);
localStorage.setItem(key, JSON.stringify(values));
}
delete(key: string, experiment: string) {
const data = JSON.parse(localStorage.getItem(key) ?? '{}');
delete data[experiment];
localStorage.setItem(key, JSON.stringify(data));
}
}
const storage = new LocalStorageUserPersistedStorage();
const adapter = new UserPersistentOverrideAdapter(storage);
const client = new StatsigClient('client-xyz', { overrideAdapter: adapter });
await client.initializeAsync({ userID: '123' });
const userPersistedValues = adapter.loadUserPersistedValues({ userID: '123' }, 'userID');
const experiment = client.getExperiment('active_experiment', { userPersistedValues });
```
See [Client Persistent Assignment](/client/concepts/persistent_assignment) for additional patterns and storage options.
## Common Targeting Use Cases
Capture cookies or URL parameters and pass them through `StatsigUser.custom` for targeting rules.
```tsx
const user = {
custom: {
isLoggedIn: cookieLib.get('isLoggedIn'),
utm: new URL(window.location.href).searchParams.get('utm'),
},
};
const client = new StatsigClient('client-xyz', user, options);
```
{% figure %}

{% /figure %}
## Async Timeouts
Limit how long `initializeAsync` and `updateUserAsync` wait for network responses before falling back to cached values.
```tsx
await client.initializeAsync({ timeoutMs: 1000 });
await client.updateUserAsync(
{ userID: 'a-user' },
{ timeoutMs: 1000 },
);
```
### Data Adapter
`StatsigClient` uses an `EvaluationsDataAdapter` to manage caching and network fetches. The default implementation (`StatsigEvaluationsDataAdapter`) reads from local storage synchronously and refreshes values from Statsig asynchronously.
Go to [Using EvaluationsDataAdapter](/client/javascript-mono/UsingEvaluationsDataAdapter) for full examples, including bootstrapping, prefetching, and custom adapters.
### Partial User Matching
Use `customUserCacheKeyFunc` with `updateUserSync` when you need to enrich a user locally without triggering a full network refresh.
```tsx
const originalUser = {
customIDs: {
analyticsID: 'analytics-123',
},
};
const customKey = (sdkKey: string, user: StatsigUser) => {
const analyticsID = user.customIDs?.analyticsID ?? 'anonymous';
return `sdkKey:$\{sdkKey\}:analyticsID:${analyticsID}`;
};
const client = new StatsigClient('client-xyz', originalUser, {
customUserCacheKeyFunc: customKey,
});
await client.initializeAsync();
someAsyncFunction().then((newData) => {
const enrichedUser = {
...originalUser,
userID: newData.userID,
email: newData.email,
};
client.updateUserSync(enrichedUser);
});
```
{% callout type="warning" %}
Custom cache keys can produce stale or incorrect evaluations if multiple users map to the same key. Await `updateUserAsync` when you need guaranteed fresh values per user.
{% /callout %}
### Client Event Emitter
Subscribe to Statsig client lifecycle events to respond to initialization, logging, or evaluation changes.
```tsx
import type {
AnyStatsigClientEvent,
StatsigClientEvent,
} from '@statsig/client-core';
const onAnyEvent = (event: AnyStatsigClientEvent) => {
console.log('Statsig event', event);
};
const onLogsFlushed = (event: StatsigClientEvent<'logs_flushed'>) => {
console.log('Logs', event.events);
};
client.on('logs_flushed', onLogsFlushed);
client.on('*', onAnyEvent);
client.off('logs_flushed', onLogsFlushed);
client.off('*', onAnyEvent);
```
| Event | Payload | Description |
| --- | --- | --- |
| `values_updated` | `{ status, values }` | Fired when initialize/update refreshes cached values. |
| `session_expired` | `{}` | Fired when the current session expires. |
| `error` | `{ error, tag }` | Unexpected client errors. |
| `pre_logs_flushed` | `{ events }` | Before a batch of events is sent. |
| `logs_flushed` | `{ events }` | After events are sent. |
| `pre_shutdown` | `{}` | Before the SDK shuts down. |
| `initialization_failure` | `{}` | Initialization failed. |
| `gate_evaluation` | `{ gate }` | When a gate is evaluated. |
| `dynamic_config_evaluation` | `{ dynamicConfig }` | When a config is evaluated. |
| `experiment_evaluation` | `{ experiment }` | When an experiment is evaluated. |
| `layer_evaluation` | `{ layer }` | When a layer is evaluated. |
| `log_event_called` | `{ event }` | When `logEvent` is called. |
## Quality & Troubleshooting
## Testing
Mock Statsig APIs in Jest to isolate business logic.
```tsx
import { StatsigClient } from '@statsig/js-client';
export async function transform(input: string): Promise {
const client = new StatsigClient('client-xyz', { userID: 'a-user' }, {
networkConfig: {
preventAllNetworkTraffic:
typeof process !== 'undefined' && process.env['NODE_ENV'] === 'test',
},
});
await client.initializeAsync();
if (client.checkGate('a_gate')) {
input = 'transformed';
}
const experiment = client.getExperiment('an_experiment');
input += '-' + experiment.get('my_param', 'fallback');
await client.shutdown();
return input;
}
```
```tsx
import { StatsigClient } from '@statsig/js-client';
jest.mock('@statsig/js-client');
test('string transformations', async () => {
jest
.spyOn(StatsigClient.prototype, 'checkGate')
.mockImplementation(() => true);
jest
.spyOn(StatsigClient.prototype, 'getExperiment')
.mockImplementation(() => ({ get: () => 'my-value' } as any));
const result = await transform('original');
expect(result).toBe('transformed-my-value');
});
```
## Debugging
When results look unexpected, use these tools to inspect what the SDK is doing.
### Enable Verbose Logging
```ts
import { LogLevel, StatsigClient } from '@statsig/js-client';
const client = new StatsigClient('client-xyz', { userID: 'a-user' }, {
logLevel: LogLevel.Debug,
});
```
### Inspect the `__STATSIG__` Global
Open your browser console and run `__STATSIG__` to inspect the current client instance. Useful properties include `_logger._queue` for pending events.
{% figure %}

{% /figure %}
### Review Network Traffic
Filter network requests by `client-` to see initialization and logging calls.
{% figure %}

{% /figure %}
### Check Evaluation Reasons
```ts
const gate = client.getFeatureGate('a_gate');
console.log(gate.details.reason);
```
Common reasons:
* `Network` | `NetworkNotModified`: latest values from the API.
* `Cache`: loaded from local storage.
* `NoValues`: no cached values and network failed.
* `Bootstrap`: values provided through `dataAdapter.setData`.
* `Prefetch`: values from `dataAdapter.prefetchData`.
Go to [`/sdk/debugging`](/sdks/debugging#reasons) for full details.
## FAQs
#### Does the SDK use local storage or cookies?
Statsig's web SDK doesn't set cookies. It stores gate/config values and unsent events in `localStorage` so features keep working when offline.
#### Can I access the SDK instance globally?
```tsx
window.Statsig.instance().logEvent('test_event');
```
```ts
import { StatsigClient } from '@statsig/js-client';
StatsigClient.instance().logEvent('test_event');
```
{% callout type="info" %}
With multiple instances, pass the SDK key: `Statsig.instance('client-YOUR_KEY')`.
{% /callout %}
#### How do I handle consent or GDPR flows?
Start with logging disabled and storage blocked, then enable them after consent.
```tsx
const client = new StatsigClient('client-xyz', {}, {
loggingEnabled: 'disabled',
disableStorage: true,
});
await client.initializeAsync();
client.updateRuntimeOptions({
loggingEnabled: 'browser-only',
disableStorage: false,
});
```
The SDK buffers up to 500 events in memory and flushes them once logging is re-enabled.
## Additional Resources
* [Client Persistent Assignment](/client/concepts/persistent_assignment)
* [Using EvaluationsDataAdapter](/client/javascript-mono/UsingEvaluationsDataAdapter)
* [Debugging SDK Evaluations](/sdks/debugging)
================================================================================
# HTML Snippet
URL: https://docs.statsig.com/client/html-snippet
Updated: 2026-07-11
================================================================================
## Set Up the SDK
{% steps %}
{% step title="Install the SDK" %}
Install the Statsig SDK by adding a script tag to the head of your HTML file:
```js
```
Replace `YOUR_CLIENT_API_KEY` with your actual client API key from [Project Settings > API Keys](https://console.statsig.com/api_keys).
{% /step %}
{% step title="Initialize the SDK" %}
The HTML snippet wraps an instance of the Statsig JavaScript SDK. Providing your client API key in the URL auto-initializes the SDK, as shown in the installation step.
Move to manual initialization if you need any of the following:
* custom user properties
* custom initialization options
* check gates, configs, experiments, or log events
To manually initialize an instance of the SDK, remove the key parameter from the script tag and do the following:
```js
const { StatsigClient, runStatsigAutoCapture, runStatsigSessionReplay } = window.Statsig;
const client = new StatsigClient(
'',
{ userID: 'a-user' }
);
runStatsigSessionReplay(client);
runStatsigAutoCapture(client);
await client.initializeAsync();
// check gates, configs, experiments, or log events
```
The `StatsigClient` instance you create is also available via `window.Statsig`, so you can reference it globally.
Your `StatsigClient` instance provides access to all methods in the [JavaScript SDK](/client/javascript-sdk). Go to that documentation for initialization details and core methods.
{% /step %}
{% /steps %}
#### Can I customize the initialization logic?
Yes. Remove the client API key from the URL. The HTML snippet is the JavaScript SDK. Providing an API key in the URL auto-initializes an instance. To skip auto-initialization, omit the key and create your own instance. Go to the [JavaScript SDK Getting Started Guide](/client/javascript-sdk#getting-started) for details.
Creating an instance using the HTML snippet differs from installing through npm:
```js
const { StatsigClient, runStatsigAutoCapture, runStatsigSessionReplay } = window.Statsig;
const client = new StatsigClient(
'',
{ userID: 'a-user' }
);
runStatsigSessionReplay(client);
runStatsigAutoCapture(client);
await client.initializeAsync();
// check gates, configs, experiments, or log events
```
================================================================================
# React Client SDK
URL: https://docs.statsig.com/client/React
Updated: 2026-07-11
================================================================================
## Set Up the SDK
{% steps %}
{% step title="Install the SDK" %}
{% callout type="info" %}
If you need a starter project, follow the official [React quickstart](https://react.dev/learn/build-a-react-app-from-scratch). Looking for Next.js instead? Go to the [Next.js SDK](/client/Next) docs.
{% /callout %}
### AI-powered Setup
Setup Statsig in 90 seconds by copying this AI prompt into your IDE:
```text expandable
You are a frontend engineer integrating the Statsig SDK into a React app. Follow these instructions carefully:
1. Install the required Statsig packages:
npm install @statsig/react-bindings @statsig/session-replay @statsig/web-analytics
2. In the main component file (`App.jsx` or `App.tsx`):
- Import `StatsigProvider` and `useClientAsyncInit` from `@statsig/react-bindings`
- Import `StatsigAutoCapturePlugin` from `@statsig/web-analytics` and `StatsigSessionReplayPlugin` from `@statsig/session-replay`
- Initialize the SDK using your client key: 'YOUR-CLIENT-API-KEY'
- Use `userID` from an existing variable if it's already declared in the file; otherwise, default to `'a-user'`
- Wrap the existing app content inside ``, using `
Loading...
` as the `loadingComponent`
3. DO NOT remove any existing JSX content from the component. Just wrap it.
4. Here is what the final file structure should look like:
import { StatsigProvider, useClientAsyncInit } from '@statsig/react-bindings';
import { StatsigAutoCapturePlugin } from '@statsig/web-analytics';
import { StatsigSessionReplayPlugin } from '@statsig/session-replay';
import YourApp from './YourApp';
function App() {
const id = typeof userID !== 'undefined' ? userID : 'a-user';
const { client } = useClientAsyncInit(
'YOUR-CLIENT-API-KEY',
{ userID: id },
{ plugins: [new StatsigAutoCapturePlugin(), new StatsigSessionReplayPlugin()] }
);
return (
Loading...}>
);
}
5. Ask the user to provide their CLIENT-API-KEY and insert it where prompted above.
```
### Install Packages
{% tabs %}
{% tab name="npm" %}
```bash
npm install @statsig/react-bindings
```
{% /tab %}
{% tab name="yarn" %}
```bash
yarn add @statsig/react-bindings
```
{% /tab %}
{% /tabs %}
{% callout type="tip" %}
Add `@statsig/session-replay` and `@statsig/web-analytics` if you plan to enable Session Replay or Auto Capture.
{% /callout %}
{% /step %}
{% step title="Initialize the SDK" %}
Next, initialize the SDK with a client SDK key from the ["API Keys" tab on the Statsig console](https://console.statsig.com/api_keys). These keys are safe to embed in a client application.
Along with the key, pass in a [User Object](#statsig-user) with the attributes you'd like to target later on in a gate or experiment.
### Wrap Your App With `StatsigProvider`
Provide your client SDK key and initial user when you render the provider.
```tsx
import { StatsigProvider } from '@statsig/react-bindings';
function App() {
return (
Hello world
);
}
```
### Typical Project Structure
Most projects render a root component inside the provider.
```tsx
// App.tsx
import RootPage from './RootPage';
import { StatsigProvider } from '@statsig/react-bindings';
export default function App() {
return (
);
}
```
```tsx
// RootPage.tsx
export default function RootPage() {
return
Hello World
;
}
```
{% callout type="info" %}
Need to balance startup speed with freshness? Review [Initialization Strategies](/client/concepts/initialize) for bootstrap and async options.
{% /callout %}
{% /step %}
{% /steps %}
## Use the SDK
Use `useStatsigClient` inside components to retrieve the client when you need to evaluate something.
```tsx
import { useStatsigClient } from '@statsig/react-bindings';
const { client } = useStatsigClient();
```
### Checking a Feature Flag/Gate
Now that your SDK is initialized, let's check a [**Feature Gate**](/feature-flags/overview). Feature Gates can be used to create logic branches in code that can be rolled out to different users from the Statsig Console. Gates are always **CLOSED** or **OFF** (think `return false;`) by default.
```tsx
import {
useFeatureGate,
useGateValue,
useStatsigClient,
} from '@statsig/react-bindings';
const { checkGate } = useStatsigClient();
const gateValue = useGateValue('my_gate');
const gate = useFeatureGate('my_gate');
return (
{checkGate('my_gate') &&
Passing
}
{gateValue &&
Passing
}
{gate.value &&
Passing ({gate.details.reason})
}
);
```
### Reading a Dynamic Config
Feature Gates are useful for simple on/off switches with optional advanced user targeting. To send a different set of values (strings, numbers, etc.) to clients based on specific user attributes such as country, use **Dynamic Configs**. The API is similar to Feature Gates, but you get a complete JSON object you can configure on the server and fetch typed parameters from it. For example:
```tsx
import { useDynamicConfig, useStatsigClient } from '@statsig/react-bindings';
const config = useDynamicConfig('my_dynamic_config');
const { getDynamicConfig } = useStatsigClient();
return (
Reason: {config.details.reason}
Value: {config.get('a_value', 'fallback_value')}
Another Value: {getDynamicConfig('my_dynamic_config').get('a_bool', false)}
);
```
### Getting a Layer/Experiment
**Layers/Experiments** let you run A/B/n experiments. Two APIs are available, but Statsig recommends [layers](/experiments/layers-overview) for quicker iterations with parameter reuse.
```tsx
import { useExperiment, useStatsigClient } from '@statsig/react-bindings';
const experiment = useExperiment('my_experiment');
const { getExperiment } = useStatsigClient();
return (
);
```
### Logging an Event
After setting up a Feature Gate or Experiment, you may want to track custom events to see how new features or experiment groups affect those events. Call the Log Event API for the event. You can also provide a value and metadata object to be logged with the event:
```tsx
import { useStatsigClient } from '@statsig/react-bindings';
const { logEvent } = useStatsigClient();
return ;
```
### Flushing Logged Events
`flush()` sends queued events immediately. Use `shutdown()` when your app is exiting.
```tsx
import { useStatsigClient } from '@statsig/react-bindings';
const { client } = useStatsigClient();
return (
);
```
## Parameter Stores
Parameter Stores hold a set of parameters for your app. These parameters can be remapped dynamically from a static value to a Statsig entity (Feature Gates, Experiments, and Layers), so you can decouple your code from the configuration in Statsig. Refer to [Parameter Stores](/client/concepts/parameter-stores) for details.
## Manage Users
### Updating User Properties
Call `updateUserAsync` when a user logs in or when you collect richer attributes.
```tsx
import { useGateValue, useStatsigUser } from '@statsig/react-bindings';
export default function AccountBanner() {
const gateValue = useGateValue('check_user');
const { updateUserAsync } = useStatsigUser();
return (
Gate is {gateValue ? 'passing' : 'failing'}.
);
}
```
## Loading State
To wait for the latest values during initialization, use either the provider or the async hook.
{% tabs %}
{% tab name="StatsigProvider" %}
```tsx
import { StatsigProvider } from '@statsig/react-bindings';
export function App() {
return (
Loading...}
>
);
}
```
{% /tab %}
{% tab name="useClientAsyncInit" %}
```tsx
import { StatsigProvider, useClientAsyncInit } from '@statsig/react-bindings';
export function App() {
const { client, isLoading } = useClientAsyncInit(
'client-KEY',
{ userID: 'a-user' },
);
if (isLoading) {
return
Loading...
;
}
return (
);
}
```
{% /tab %}
{% /tabs %}
## React Hooks
{% callout type="warning" %}
Hooks that read gates, configs, experiments, or layers will log exposures on render. Use `useStatsigClient` to defer checks until you actually change the UI.
{% /callout %}
### Feature Gate Hooks
* Recommended: `useStatsigClient().checkGate` logs when invoked.
* `useGateValue` returns the boolean value and logs immediately.
* `useFeatureGate` returns the full gate object with details.
```tsx
import {
useFeatureGate,
useGateValue,
useStatsigClient,
} from '@statsig/react-bindings';
const { checkGate } = useStatsigClient();
const gateValue = useGateValue('my_gate');
const gate = useFeatureGate('my_gate');
return (
);
}
function App() {
return (
);
}
```
```tsx
import { render, screen } from '@testing-library/react';
import * as ReactBindings from '@statsig/react-bindings';
jest.mock('@statsig/react-bindings', () => ({
...jest.requireActual('@statsig/react-bindings'),
useFeatureGate: () => ({ value: true }),
useExperiment: () => ({ get: () => 'my_value' }),
}));
test('renders gate pass', async () => {
render();
const elem = await screen.findByTestId('gate_test');
expect(elem.textContent).toContain('Pass');
});
test('renders experiment value', async () => {
render();
const elem = await screen.findByTestId('exp_test');
expect(elem.textContent).toContain('my_value');
});
```
## Lifecycle & Advanced Usage
## Shutting Statsig Down
The SDK keeps event logs in the client cache and flushes them periodically to save data and battery usage. Because of this, the SDK may not have flushed some events when your app shuts down.
To ensure all logged events are flushed or saved locally, shut down Statsig when your app is closing:
```tsx
import { useEffect } from 'react';
import { useStatsigClient } from '@statsig/react-bindings';
const { client } = useStatsigClient();
useEffect(() => {
return () => {
void client.shutdown();
};
}, [client]);
```
## Session Replay
Install `@statsig/session-replay` and register the plugin to record user sessions.
```tsx
import { StatsigProvider, useClientAsyncInit } from '@statsig/react-bindings';
import { StatsigSessionReplayPlugin } from '@statsig/session-replay';
function App() {
const { client } = useClientAsyncInit(
'client-KEY',
{ userID: 'a-user' },
{ plugins: [new StatsigSessionReplayPlugin()] },
);
return (
Loading...}>
Hello World
);
}
```
## Web Analytics / Auto Capture
By including the [`@statsig/web-analytics`](https://www.npmjs.com/package/@statsig/web-analytics) package in your project, you can automatically capture common web events like clicks and page views.
For more information on filtering events, enabling console log capture, and other configuration options available in web analytics, refer to the [Web Analytics Configuration](/webanalytics/overview#event-filtering-and-console-configuration) documentation.
```tsx
import { StatsigProvider, useClientAsyncInit } from '@statsig/react-bindings';
import { StatsigAutoCapturePlugin } from '@statsig/web-analytics';
function App() {
const { client } = useClientAsyncInit(
'client-KEY',
{ userID: 'a-user' },
{ plugins: [new StatsigAutoCapturePlugin()] },
);
return (
Loading...}>
Hello World
);
}
```
## Using Persistent Evaluations
To keep experiment variants stable across rerenders or user transitions, use persistent storage. The React integration mirrors the [JavaScript workflow](/client/javascript-sdk#using-persistent-evaluations) and you can adapt the [Next.js sample](https://github.com/statsig-io/js-client-monorepo/tree/main/samples/next-js/src/app/persisted-user-storage-example) to your setup.
Read more in [Client Persistent Assignment](/client/concepts/persistent_assignment).
## Additional Resources
* [Initialization Concepts](/client/concepts/initialize)
* [JavaScript Client SDK](/client/javascript-sdk)
* [Persistent Assignment](/client/concepts/persistent_assignment)
================================================================================
# Next.js Client SDK
URL: https://docs.statsig.com/client/Next
Updated: 2026-07-11
================================================================================
## Set Up the SDK
### AI-powered Setup
Setup Statsig in 90 seconds by copying this AI prompt into your IDE:
```text expandable Prompt
# Statsig SDK Integration for Next.js
You are a frontend engineer integrating the Statsig SDK into a **Next.js application**. Follow all steps below one by one:
---
## Full Integration Instructions
1. **Detect the package manager** by checking for:
- `package-lock.json` → use `npm`
- `yarn.lock` → use `yarn`
- `pnpm-lock.yaml` → use `pnpm`
2. **Detect the Next.js router type** by checking for:
- `app/` directory → **App Router**
- `pages/` directory → **Pages Router**
3. **Install the Statsig package** using the correct package manager:
# For npm
npm install @statsig/react-bindings @statsig/session-replay @statsig/web-analytics
# For yarn
yarn add @statsig/react-bindings @statsig/session-replay @statsig/web-analytics
# For pnpm
pnpm add @statsig/react-bindings @statsig/session-replay @statsig/web-analytics
4. Add your Statsig client key to .env.local:
NEXT_PUBLIC_STATSIG_CLIENT_KEY=ask the user for their CLIENT KEY and use that input
5. Integrate Statsig into the app (auto-detect router type):
### If the project uses the App Router (has an app/ directory):
// Create app/my-statsig.tsx
"use client";
import React from "react";
import { LogLevel, StatsigProvider } from "@statsig/react-bindings";
export default function MyStatsig({ children }: { children: React.ReactNode }) {
const id = typeof userID !== "undefined" ? userID : "a-user";
const user = {
userID: id,
// Optional additional fields:
// email: 'user@example.com',
// customIDs: { internalID: 'internal-123' },
// custom: { plan: 'premium' }
};
return (
{children}
);
}
// Update app/layout.tsx to wrap children with MyStatsig
import MyStatsig from "./my-statsig";
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
return (
{children} {/* Preserve all existing layout content */}
);
}
### If the project uses the Pages Router (has a pages/ directory):
// Update pages/_app.tsx
import type { AppProps } from "next/app";
import { LogLevel, StatsigProvider } from "@statsig/react-bindings";
export default function App({ Component, pageProps }: AppProps) {
const id = typeof userID !== "undefined" ? userID : "a-user";
const user = {
userID: id,
// Optional additional fields:
// email: 'user@example.com',
// customIDs: { internalID: 'internal-123' },
// custom: { plan: 'premium' }
};
return (
{/* Preserve all existing pages */}
);
}
### Final Notes
- The system must **detect the router type** and **apply the correct integration automatically**.
- **Do not remove or change any existing JSX or layout structure**: only wrap the app with `StatsigProvider`.
- **Preserve the file's language**: if it's TypeScript (`.tsx`), keep it as TypeScript; if it's JavaScript (`.jsx`), keep it as JavaScript.
- After these steps, Statsig will be integrated across the entire app, with **feature gates, configs, and experiments** available everywhere.
```
### Manual Setup
Statsig supports both [Page Router](https://nextjs.org/docs/pages) & [App Router](https://nextjs.org/docs/app), with some differences in integration patterns.
{% tabs %}
{% tab title="App Router" %}
{% steps %}
{% step title="Set environment variables" %}
Add the keys to your .env.local file:
```bash .env.local
# the NEXT_PUBLIC_ prefix is required for this to be available on the client side
NEXT_PUBLIC_STATSIG_CLIENT_KEY=client-
STATSIG_SERVER_KEY=secret-
```
{% /step %}
{% step title="Install packages" %}
For App Router, install the @statsig/next package:
{% codetabs %}
```bash NPM
npm i @statsig/next
```
```bash Yarn
yarn add @statsig/next
```
```bash PNPM
pnpm add @statsig/next
```
{% /codetabs %}
{% /step %}
{% step title="Add the StatsigBootstrapProvider" %}
The \ creates both a Statsig Client and Server instance internally, and ["bootstraps"](/client/concepts/initialize#2-bootstrap-initialization) the client so it can render each page without a blocking network request. This keeps your app fast and is recommended for most users. If you need more control over your setup, refer to [Bootstrapping](#client-bootstrapping-recommended) and [React](/client/React) for more guidance.
Add this component around the content in your root `layout.tsx` file:
```tsx app/layout.tsx
import { StatsigBootstrapProvider } from "@statsig/next"
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const user = {
userID: "user-123", // add additional parameters as needed
};
return (
{children}
);
}
```
{% /step %}
{% /steps %}
{% /tab %}
{% tab title="Page Router" %}
{% steps %}
{% step title="Set environment variables" %}
Add the keys to your .env.local file:
```bash .env.local
# the NEXT_PUBLIC_ prefix is required for this to be available on the client side
NEXT_PUBLIC_STATSIG_CLIENT_KEY=client-
```
{% /step %}
{% step title="Install packages" %}
Install the @statsig/react-bindings package:
{% codetabs %}
```bash NPM
npm i @statsig/react-bindings @statsig/web-analytics
```
```bash Yarn
yarn add @statsig/react-bindings @statsig/web-analytics
```
```bash PNPM
pnpm add @statsig/react-bindings @statsig/web-analytics
```
{% /codetabs %}
{% /step %}
{% step title="Add StatsigProvider to _app.tsx" %}
To integrate Statsig into your Page Router app you can add the `StatsigProvider` to your `_app.tsx` file.
There is a [full example](https://github.com/statsig-io/js-client-monorepo/tree/main/samples/next-js-pages-router-sample) in the samples directory of the javascript sdk.
```tsx pages/_app.tsx
import type { AppProps } from "next/app";
import {
LogLevel,
StatsigProvider,
} from "@statsig/react-bindings";
import { StatsigAutoCapturePlugin } from '@statsig/web-analytics';
export default function App({ Component, pageProps }: AppProps) {
return (
);
}
```
{% /step %}
{% /steps %}
{% /tab %}
{% /tabs %}
Refer to the [User (StatsigUser)](/sdks/user) documentation for more information on the user property. After completing setup, you can check gates, experiments, and log events in any sub-file of layout.tsx.
## Use the SDK
### Checking a Feature Flag/Gate
Now that your SDK is initialized, let's check a [**Feature Gate**](/feature-flags/overview). Feature Gates can be used to create logic branches in code that can be rolled out to different users from the Statsig Console. Gates are always **CLOSED** or **OFF** (think `return false;`) by default.
{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';
import { useGateValue } from "@statsig/react-bindings";
export default function Home() {
const gate = useGateValue("my_gate");
return (
Gate Value: {gate ? 'PASSED' : 'FAILED'}
);
}
```
{% callout type="note" %}
In an App Router app, you need to use the [`use client` directive](https://nextjs.org/docs/app/building-your-application/rendering/client-components) to ensure your logic runs on the frontend.
{% /callout %}
{% /tab %}
{% tab title="Page Router" %}
```tsx
import { useGateValue } from "@statsig/react-bindings";
export default function Home() {
const gate = useGateValue("my_gate");
return (
Gate Value: {gate ? 'PASSED' : 'FAILED'}
);
}
```
{% /tab %}
{% /tabs %}
### Reading a Dynamic Config
Feature Gates are useful for simple on/off switches with optional advanced user targeting. To send a different set of values (strings, numbers, etc.) to clients based on specific user attributes such as country, use **Dynamic Configs**. The API is similar to Feature Gates, but you get a complete JSON object you can configure on the server and fetch typed parameters from it. For example:
{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';
import { useDynamicConfig } from "@statsig/react-bindings";
export default function Home() {
const config = useDynamicConfig("my_dynamic_config");
return (
Title: {config.get('title', 'Fallback Title')}
);
}
```
{% callout type="note" %}
In an App Router app, you need to use the [`use client` directive](https://nextjs.org/docs/app/building-your-application/rendering/client-components) to ensure your logic runs on the frontend.
{% /callout %}
{% /tab %}
{% tab title="Page Router" %}
```tsx
import { useDynamicConfig } from "@statsig/react-bindings";
export default function Home() {
const config = useDynamicConfig("my_dynamic_config");
return (
Title: {config.get('title', 'Fallback Title')}
);
}
```
{% /tab %}
{% /tabs %}
### Getting a Layer/Experiment
**Layers/Experiments** let you run A/B/n experiments. Two APIs are available, but Statsig recommends [layers](/experiments/layers-overview) for quicker iterations with parameter reuse.
{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';
import { useExperiment, useLayer} from "@statsig/react-bindings";
export default function Home() {
const layer = useLayer("my_experiment_layer");
// or
const experiment = useExperiment("my_experiment");
return (
);
}
```
{% callout type="note" %}
In an App Router app, you need to use the [`use client` directive](https://nextjs.org/docs/app/building-your-application/rendering/client-components) to ensure your logic runs on the frontend.
{% /callout %}
{% /tab %}
{% tab title="Page Router" %}
```tsx
import { useExperiment, useLayer} from "@statsig/react-bindings";
export default function Home() {
const layer = useLayer("my_experiment_layer");
// or
const experiment = useExperiment("my_experiment");
return (
);
}
```
{% /tab %}
{% /tabs %}
## Parameter Stores
Parameter Stores hold a set of parameters for your app. These parameters can be remapped dynamically from a static value to a Statsig entity (Feature Gates, Experiments, and Layers), so you can decouple your code from the configuration in Statsig. Refer to [Parameter Stores](/client/concepts/parameter-stores) for details.
{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';
import { useParameterStore} from "@statsig/react-bindings";
export default function Home() {
const store = useParameterStore("my_param_store");
return (
Title: {store.get('title', 'Fallback Title')}
);
}
```
{% callout type="note" %}
In an App Router app, you need to use the [`use client` directive](https://nextjs.org/docs/app/building-your-application/rendering/client-components) to ensure your logic runs on the frontend.
{% /callout %}
{% /tab %}
{% tab title="Page Router" %}
```tsx
import { useParameterStore} from "@statsig/react-bindings";
export default function Home() {
const store = useParameterStore("my_param_store");
return (
Title: {store.get('title', 'Fallback Title')}
);
}
```
{% /tab %}
{% /tabs %}
### Logging an Event
After setting up a Feature Gate or Experiment, you may want to track custom events to see how new features or experiment groups affect those events. Call the Log Event API for the event. You can also provide a value and metadata object to be logged with the event:
{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';
import { useStatsigClient } from "@statsig/react-bindings";
export default function Home() {
const { client } = useStatsigClient();
return (
);
}
```
{% callout type="note" %}
In an App Router app, you need to use the [`use client` directive](https://nextjs.org/docs/app/building-your-application/rendering/client-components) to ensure your logic runs on the frontend.
{% /callout %}
{% /tab %}
{% tab title="Page Router" %}
```tsx
import { useStatsigClient } from "@statsig/react-bindings";
export default function Home() {
const { client } = useStatsigClient();
return (
);
}
```
{% /tab %}
{% /tabs %}
### Flushing Logged Events
`flush()` sends queued events immediately. Use `shutdown()` when your app is exiting.
{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';
import { useStatsigClient } from "@statsig/react-bindings";
export default function Home() {
const { client } = useStatsigClient();
return (
);
}
```
{% callout type="note" %}
In an App Router app, you need to use the [`use client` directive](https://nextjs.org/docs/app/building-your-application/rendering/client-components) to ensure your logic runs on the frontend.
{% /callout %}
{% /tab %}
{% tab title="Page Router" %}
```tsx
import { useStatsigClient } from "@statsig/react-bindings";
export default function Home() {
const { client } = useStatsigClient();
return (
);
}
```
{% /tab %}
{% /tabs %}
## Session Replay
{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';
import { StatsigProvider } from '@statsig/react-bindings';
import { StatsigSessionReplayPlugin } from '@statsig/session-replay';
export default function App({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
{% /tab %}
{% tab title="Page Router" %}
```tsx
import { StatsigProvider } from '@statsig/react-bindings';
import { StatsigSessionReplayPlugin } from '@statsig/session-replay';
export default function App({ Component, pageProps }) {
return (
);
}
```
{% /tab %}
{% /tabs %}
## Web Analytics / Auto Capture
By including the [`@statsig/web-analytics`](https://www.npmjs.com/package/@statsig/web-analytics) package in your project, you can automatically capture common web events like clicks and page views.
For more information on filtering events, enabling console log capture, and other configuration options available in web analytics, refer to the [Web Analytics Configuration](/webanalytics/overview#event-filtering-and-console-configuration) documentation.
{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';
import { StatsigProvider } from '@statsig/react-bindings';
import { StatsigAutoCapturePlugin } from '@statsig/web-analytics';
export default function App({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
{% /tab %}
{% tab title="Page Router" %}
```tsx
import { StatsigProvider } from '@statsig/react-bindings';
import { StatsigAutoCapturePlugin } from '@statsig/web-analytics';
export default function App({ Component, pageProps }) {
return (
);
}
```
{% /tab %}
{% /tabs %}
## Stable ID
Stable ID provides a consistent device identifier. It lets you run [logged-out experiments](/guides/first-device-level-experiment) and target gates at the device level.
### How Stable ID Works
* On first initialization, the SDK generates a Stable ID and stores it in `localStorage` under `statsig.stable_id.`.
* Subsequent sessions reuse the stored value. Each client SDK key has its own Stable ID entry.
* Local storage is scoped per domain, so cross-domain usage requires sharing the value manually (see below).
### Reading the Stable ID
{% tabs %}
{% tab title="JavaScript" %}
```tsx
const context = client.getContext();
console.log('Statsig StableID:', context.stableID);
```
{% /tab %}
{% tab title="React" %}
```tsx
import { useStatsigClient } from '@statsig/react-bindings';
function MyComponent() {
const { client } = useStatsigClient();
const context = client.getContext();
return
{context.stableID}
;
}
```
{% /tab %}
{% /tabs %}
### Overriding the Stable ID
Provide a custom Stable ID through `StatsigUser.customIDs.stableID` if you already manage a durable device identifier.
{% tabs %}
{% tab title="JavaScript" %}
```tsx
import { StatsigClient, StatsigUser } from '@statsig/js-client';
const userWithStableID: StatsigUser = {
customIDs: {
stableID: 'my-custom-stable-id',
},
};
const client = new StatsigClient('client-xyz', userWithStableID);
await client.updateUserAsync(userWithStableID);
```
{% /tab %}
{% tab title="React" %}
```tsx
import { StatsigProvider, useStatsigClient } from '@statsig/react-bindings';
function App() {
return (
Your App
);
}
function MyComponent() {
const { client } = useStatsigClient();
useEffect(() => {
client.updateUserAsync({
customIDs: { stableID: 'my-custom-stable-id' },
});
}, [client]);
}
```
{% /tab %}
{% /tabs %}
{% callout type="note" %}
When you override the Stable ID, it is persisted to local storage, so subsequent sessions reuse your custom value.
{% /callout %}
### Sharing Stable ID Across Subdomains
Add this helper script before initializing the SDK and then copy the stored value onto your user object.
```html
```
_Use this script at your discretion and test thoroughly._
### Aligning Stable ID Between Client and Server
To share Stable ID with a backend Statsig SDK, send the value with requests and persist it server-side when missing. The server can bootstrap the client with the same Stable ID.
```tsx
// Server: ensure Stable ID exists, then return initialize response for the client
const values = Statsig.getClientInitializeResponse(user, YOUR_CLIENT_KEY, {
hash: 'djb2',
});
// Client: apply the server-provided values and initialize synchronously
const { values, user: verifiedUser } = await fetch('/init-statsig-client', {
method: 'POST',
body: loadUserData(),
}).then((res) => res.json());
const myClient = new StatsigClient(YOUR_CLIENT_KEY, verifiedUser);
myClient.dataAdapter.setData(values);
myClient.initializeSync();
```
## Lifecycle & Advanced Usage
## Shutting Statsig Down
The SDK keeps event logs in the client cache and flushes them periodically to save data and battery usage. Because of this, the SDK may not have flushed some events when your app shuts down.
To ensure all logged events are flushed or saved locally, shut down Statsig when your app is closing:
{% tabs %}
{% tab title="App Router" %}
```tsx
'use client';
import { useEffect } from "react";
import { useStatsigClient } from "@statsig/react-bindings";
export default function Home() {
const { client } = useStatsigClient();
useEffect(() => {
return () => {
void client.shutdown();
};
}, [client]);
return null;
}
```
{% callout type="note" %}
In an App Router app, you need to use the [`use client` directive](https://nextjs.org/docs/app/building-your-application/rendering/client-components) to ensure your logic runs on the frontend.
{% /callout %}
{% /tab %}
{% tab title="Page Router" %}
```tsx
import { useEffect } from "react";
import { useStatsigClient } from "@statsig/react-bindings";
export default function Home() {
const { client } = useStatsigClient();
useEffect(() => {
return () => {
void client.shutdown();
};
}, [client]);
return null;
}
```
{% /tab %}
{% /tabs %}
## Advanced Setup
### Client Bootstrapping (Recommended)
{% tabs %}
{% tab title="App Router" %}
```ts app/api/statsig-bootstrap/route.ts
import { Statsig, StatsigUser } from '@statsig/statsig-node-core';
export async function POST(request: Request): Promise {
const body = await request.json();
const user = new StatsigUser(body?.user ?? {});
// Ensure server SDK is initialized at startup
// await Statsig.initialize(process.env.STATSIG_SERVER_KEY!);
const values = Statsig.getClientInitializeResponse(user, {
hashAlgorithm: 'djb2',
});
return new Response(JSON.stringify(values), { status: 200 });
}
```
```tsx app/layout.tsx
import { StatsigBootstrapProvider } from '@statsig/next';
export default function RootLayout({ children }: { children: React.ReactNode }) {
const user = { userID: 'user-123' };
return (
{children}
);
}
```
{% /tab %}
{% tab title="Page Router" %}
```ts pages/api/statsig-bootstrap.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { Statsig, StatsigUser } from 'statsig-node'; // legacy Node SDK for pages router
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method !== 'POST') {
res.status(400).send('/statsig-bootstrap only supports POST');
return;
}
// Ensure server SDK is initialized at startup
// await Statsig.initialize(process.env.STATSIG_SERVER_KEY!);
const { user } = JSON.parse(req.body) as { user: StatsigUser };
const values = Statsig.getClientInitializeResponse(user, { hash: 'djb2' });
res.status(200).send(JSON.stringify(values));
}
```
```tsx pages/_app.tsx
import type { AppProps } from 'next/app';
import { StatsigProvider } from '@statsig/react-bindings';
import { StatsigClient } from '@statsig/js-client';
import { useEffect, useMemo, useState } from 'react';
export default function App({ Component, pageProps }: AppProps) {
const user = useMemo(() => ({ userID: 'a-user' }), []);
const [client, setClient] = useState(null);
useEffect(() => {
(async () => {
const res = await fetch('/api/statsig-bootstrap', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ user }),
});
const initializeValues = await res.json();
const inst = new StatsigClient(
process.env.NEXT_PUBLIC_STATSIG_CLIENT_KEY!,
user,
{ initializeValues },
);
await inst.initializeAsync();
setClient(inst);
})();
}, [user]);
if (!client) {
return null;
}
return (
);
}
```
{% /tab %}
{% /tabs %}
### Proxying Network Traffic (Optional)
{% tabs %}
{% tab title="App Router" %}
```ts app/proxy/initialize/route.ts
// Note: Using generic path names like "proxy" instead of "statsig-proxy"
// to prevent ad blockers from blocking these requests
import { generateBootstrapValues } from './statsig-backend';
export async function POST(request: Request): Promise {
const json = await request.json();
if (!json || typeof json !== 'object') {
return new Response(null, { status: 400 });
}
const data = await generateBootstrapValues();
return new Response(data);
}
```
```ts app/proxy/search.ts
// Note: Using generic path names like "search" instead of "log_event" or "events"
// to prevent ad blockers from blocking these requests
type ExtendedRequestInit = RequestInit & { duplex?: 'half' | 'full' };
export async function POST(request: Request): Promise {
const tail = request.url.split('?').pop();
const logEventUrl = `https://events.statsigapi.net/v1/log_event?${tail}`;
const fetchOptions: ExtendedRequestInit = {
method: 'POST',
body: request.body,
headers: request.headers,
duplex: 'half',
};
return fetch(logEventUrl, fetchOptions);
}
```
{% /tab %}
{% tab title="Page Router" %}
```ts pages/api/proxy/initialize.ts
// Note: Using generic path names like "proxy" instead of "statsig-proxy"
// to prevent ad blockers from blocking these requests
import type { NextApiRequest, NextApiResponse } from 'next';
import { StatsigUser } from 'statsig-node';
import { getStatsigValues } from '@/pages/statsig-backend';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method !== 'POST') {
res.status(400).send('/initialize only supports POST');
return;
}
const { user } = JSON.parse(req.body) as { user: StatsigUser };
const values = await getStatsigValues(user);
res.status(200).send(values);
}
```
```ts pages/api/proxy/search.ts
// Note: Using generic path names like "search" instead of "log_event" or "events"
// to prevent ad blockers from blocking these requests
import type { NextApiRequest, NextApiResponse } from 'next';
type ExtendedRequestInit = RequestInit & { duplex?: 'half' | 'full' };
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
): Promise {
if (req.method !== 'POST') {
res.status(400).send('/search only supports POST');
return;
}
let logEventUrl = `https://events.statsigapi.net/v1/log_event`;
const queryParams = [] as string[];
for (const [key, value] of Object.entries(req.query)) {
queryParams.push(`$\{key\}=${value}`);
}
if (queryParams.length > 0) {
logEventUrl += '?' + queryParams.join('&');
}
const fetchOptions: ExtendedRequestInit = {
method: 'POST',
body: req.body as BodyInit,
headers: req.headers as HeadersInit,
duplex: 'half',
};
try {
const response = await fetch(logEventUrl, fetchOptions);
if (!response.ok) {
res.status(500).send('Failed to log event');
return;
}
const body = await response.text();
res.status(response.status).send(body);
} catch (err) {
res.status(500).send('Failed to log event: ' + err);
}
}
```
{% /tab %}
{% /tabs %}
```ts
// Assign URLs when creating the client
const inst = new StatsigClient(clientSdkKey, user, {
networkConfig: {
logEventUrl: '/api/proxy/search',
initializeUrl: '/api/proxy/initialize',
logEventCompressionMode: 'Forced',
},
disableCompression: true,
disableStatsigEncoding: true,
});
```
## Static Site Generation (SSG)
Static Site Generation renders HTML at build time. Because static HTML can't respond to per-user values, experimenting on SSG content requires one of these patterns:
* Use Edge Middleware with Statsig's Edge Config Adapter for zero-latency redirects.
* Isolate Statsig usage to hydrated client components only.
```tsx
// Create a single client and share it across multiple StatsigProviders
const myStatsigClient = new StatsigClient(YOUR_SDK_KEY, user, options);
await myStatsigClient.initializeAsync();
```
## Statsig Options
{% parameter name="loggingEnabled" type="LoggingEnabledOption" %}
Controls logging behavior.
* `browser-only` (default): log events from browser environments.
* `disabled`: never send events.
* `always`: log in every environment, including non-browser contexts.
{% /parameter %}
{% parameter name="disableLogging" type="boolean" %}
Use `loggingEnabled: 'disabled'` instead.
{% /parameter %}
{% parameter name="disableStableID" type="boolean" %}
Skip generating a device-level Stable ID.
{% /parameter %}
{% parameter name="disableEvaluationMemoization" type="boolean" %}
Recompute every evaluation instead of using the memoized result.
{% /parameter %}
{% parameter name="initialSessionID" type="string" %}
Override the generated session ID.
{% /parameter %}
{% parameter name="enableCookies" type="boolean" %}
Persist Stable ID in cookies for cross-domain tracking.
{% /parameter %}
{% parameter name="disableStorage" type="boolean" %}
Prevent any local storage writes (disables caching).
{% /parameter %}
{% parameter name="networkConfig" type="NetworkConfig" %}
Override network endpoints per request type.
{% /parameter %}
{% accordion title="Network Config Options" %}
{% parameter name="api" type="string" %}
Base URL for all requests. The SDK appends endpoint paths like `/initialize` and `/rgstr`; append `/v1` when your proxy expects it.
{% /parameter %}
{% parameter name="initializeUrl" type="string" %}
Endpoint for initialization requests only. Takes precedence over `api` for `/initialize`.
{% /parameter %}
{% parameter name="initializeFallbackUrls" type="string[]" %}
Fallback endpoints for initialization requests only. This doesn't create a generic fallback for `api`.
{% /parameter %}
{% parameter name="logEventUrl" type="string" %}
Endpoint for event uploads.
{% /parameter %}
{% parameter name="logEventFallbackUrls" type="string[]" %}
Fallback endpoints for event uploads only. This doesn't create a generic fallback for `api`.
{% /parameter %}
{% parameter name="networkTimeoutMs" type="number" %}
Request timeout in milliseconds.
{% /parameter %}
{% parameter name="preventAllNetworkTraffic" type="boolean" %}
Disable all outbound requests; combine with `loggingEnabled: 'disabled'` to silence log warnings.
{% /parameter %}
{% parameter name="networkOverrideFunc" type="function" %}
Provide custom transport (e.g., Axios).
{% /parameter %}
{% /accordion %}
{% parameter name="environment" type="StatsigEnvironment" %}
Set environment-wide defaults (for example `{ tier: 'staging' }`).
{% /parameter %}
{% parameter name="logLevel" type="LogLevel" %}
Console verbosity.
{% /parameter %}
{% parameter name="loggingBufferMaxSize" type="number" %}
Max events per log batch.
{% /parameter %}
{% parameter name="loggingIntervalMs" type="number" %}
Interval between automatic flushes.
{% /parameter %}
{% parameter name="overrideAdapter" type="OverrideAdapter" %}
Modify evaluations before returning them.
{% /parameter %}
{% parameter name="includeCurrentPageUrlWithEvents" type="boolean" %}
Attach the current page URL to logged events.
{% /parameter %}
{% parameter name="disableStatsigEncoding" type="boolean" %}
Send requests without Statsig-specific encoding.
{% /parameter %}
{% parameter name="logEventCompressionMode" type="LogEventCompressionMode" %}
Control compression for batched events.
{% /parameter %}
{% parameter name="disableCompression" type="boolean" %}
Use `logEventCompressionMode` instead.
{% /parameter %}
{% parameter name="dataAdapter" type="EvaluationsDataAdapter" %}
Provide a custom data adapter to control caching/fetching.
{% /parameter %}
{% parameter name="customUserCacheKeyFunc" type="CustomCacheKeyGenerator" %}
Override cache key generation for stored evaluations.
{% /parameter %}
## Additional Resources
* [JavaScript Client SDK](/client/javascript-sdk)
* [React Client SDK](/client/React)
* [Initialization Concepts](/client/concepts/initialize)
================================================================================
# Android Client SDK
URL: https://docs.statsig.com/client/Android
Updated: 2026-07-11
================================================================================
## Set up the SDK
{% steps %}
{% step title="Install the SDK" %}
v4.37.1 and higher are published to only [Maven Central](https://central.sonatype.com/artifact/com.statsig/android-sdk). To install the SDK, set the Maven Central repository in your build.gradle.
```java
dependencies {
implementation "com.statsig:android-sdk:4.37.1"
}
```
You can install legacy versions (\<=V4.37.0) with [Jitpack](https://jitpack.io/#statsig-io/android-sdk).
{% /step %}
{% step title="Initialize the SDK" %}
Next, initialize the SDK with a client SDK key from the ["API Keys" tab on the Statsig console](https://console.statsig.com/api_keys). These keys are safe to embed in a client application.
Along with the key, pass in a [User Object](#statsig-user) with the attributes you'd like to target later on in a gate or experiment.
{% codetabs %}
```java MainActivity.java
import com.statsig.androidsdk.*;
...
public class MainActivity extends AppCompatActivity implements IStatsigCallback {
...
StatsigOptions options = new StatsigOptions();
options.setTier(Tier.PRODUCTION);
StatsigUser user = new StatsigUser("UUID");
Statsig.initializeAsync(app, "client-key", user, this, options);
...
// SDK is usable, but values will be from the cache or defaults (false for gates, {} for configs)
// Once onStatsigInitialize fires, then
@Override
public void onStatsigInitialize() {
// SDK is initialized and has the most up to date values
}
@Override
public void onStatsigUpdateUser() {
// User has been updated and values have been refetched for the new user
}
}
```
```kotlin MainActivity.kt
import com.statsig.androidsdk.*
...
async {
Statsig.initialize(
this.application,
"my_client_sdk_key",
StatsigUser("user_id"),
)
}.await()
```
{% /codetabs %}
{% /step %}
{% /steps %}
## Use the SDK
### Checking a Feature Flag/Gate
After the SDK is initialized, check a [**Feature Gate**](/feature-flags/overview). Feature Gates create logic branches in code that can be rolled out to different users from the Statsig Console. Gates are always **CLOSED** or **OFF** (equivalent to `return false;`) by default.
{% codetabs %}
```java Java
DynamicConfig config = Statsig.getConfig("awesome_product_details");
// The 2nd parameter is the default value to be used in case the given parameter name does not exist on
// the Dynamic Config object. This can happen when there is a typo, or when the user is offline and the
// value has not been cached on the client.
String itemName = config.getString("product_name", "Awesome Product v1");
Double price = config.getDouble("price", 10.0);
Boolean shouldDiscount = config.getBoolean("discount", false);
```
```kotlin Kotlin
val config = Statsig.getConfig("awesome_product_details")
// The 2nd parameter is the default value to be used in case the given parameter name does not exist on
// the Dynamic Config object. This can happen when there is a typo, or when the user is offline and the
// value has not been cached on the client.
val itemName = config.getString("product_name", "Awesome Product v1")
val price = config.getDouble("price", 10.0)
val shouldDiscount = config.getBoolean("discount", false)
```
{% /codetabs %}
### Reading a Dynamic Config
Feature Gates work well for simple on/off switches with optional advanced user targeting. To send a different set of values (strings, numbers, and so on) to clients based on specific user attributes such as country, use **Dynamic Configs**. The API is similar to Feature Gates, but returns an entire JSON object you configure on the server, from which you can fetch typed parameters. For example:
{% codetabs %}
```java Java
if (Statsig.checkGate("new_homepage_design")) {
// Gate is on, show new home page
} else {
// Gate is off, show old home page
}
```
```kotlin Kotlin
if (Statsig.checkGate("new_homepage_design")) {
// Gate is on, show new home page
} else {
// Gate is off, show old home page
}
```
{% /codetabs %}
### Getting a Layer/Experiment
**Layers/Experiments** support running A/B/n experiments. Two APIs are available, but [layers](/experiments/layers-overview) are recommended to enable quicker iterations with parameter reuse.
{% codetabs %}
```java Java
// Values via getLayer
Layer layer = Statsig.getLayer("user_promo_experiments")
String promoTitle = layer.getString("title", "Welcome to Statsig!");
Double discount = layer.getDouble("discount", 0.1);
// or, via getExperiment
DynamicConfig titleExperiment = Statsig.getExperiment("new_user_promo_title");
DynamicConfig priceExperiment = Statsig.getExperiment("new_user_promo_price");
String promoTitle = titleExperiment.getString("title", "Welcome to Statsig!");
Double discount = priceExperiment.getDouble("discount", 0.1);
...
Double price = msrp * (1 - discount);
```
```kotlin Kotlin
// Values via getLayer
val layer = Statsig.getLayer("user_promo_experiments")
val promoTitle = layer.getString("title", "Welcome to Statsig!")
val discount = layer.getDouble("discount", 0.1)
// or, via getExperiment
val titleExperiment = Statsig.getExperiment("new_user_promo_title")
val priceExperiment = Statsig.getExperiment("new_user_promo_price")
val promoTitle = titleExperiment.getString("title", "Welcome to Statsig!")
val discount = priceExperiment.getDouble("discount", 0.1)
...
val price = msrp * (1 - discount);
```
{% /codetabs %}
### Logging an Event
After setting up a Feature Gate or an Experiment, you can track custom events to see how your new features or different experiment groups affect those events. Call the Log Event API for the event, and optionally provide a value and metadata object to be logged with the event:
{% codetabs %}
```java Java
Statsig.logEvent("purchase", 2.99, Map.of("item_name", "remove_ads"));
```
```kotlin Kotlin
Statsig.logEvent("purchase", 2.99, Map.of("item_name" to "remove_ads"))
```
{% /codetabs %}
## Parameter Stores
Parameter Stores hold a set of parameters for your mobile app. These parameters can be remapped dynamically from a static value to a Statsig entity (Feature Gates, Experiments, and Layers), so you can decouple your code from the configuration in Statsig. Refer to [Param Stores](/client/concepts/parameter-stores) for more information.
### Getting a parameter store
To fetch a set of parameters, use the following API:
{% codetabs %}
```java Java
ParameterStore homepageStore = Statsig.getParameterStore("homepage");
```
```kotlin Kotlin
val homepageStore = Statsig.getParameterStore("homepage")
```
{% /codetabs %}
### Getting a parameter
You can then access parameters like this:
{% codetabs %}
```java Java
String title = homepageStore.getString(
"title", //parameter name
"Welcome" // default value
);
boolean shouldShowUpsell = homePageStore.getBoolean("upsell_upgrade_now", false);
```
```kotlin Kotlin
val title = homepageStore.getString(
"title", // parameter name
"Welcome", // default value
)
val shouldShowUpsell = homepageStore.getBoolean("upsell_upgrade_now", false)
```
{% /codetabs %}
## Statsig User
You need to provide a StatsigUser object to check or get your configurations. Pass as much information as possible to take advantage of advanced gate and config conditions.
The `userID` field is required in most cases to provide a consistent experience for a given user (refer to [logged-out experiments](/guides/first-device-level-experiment) for how to run experiments for logged-out users).
In addition to `userID`, the following top-level fields are available on StatsigUser: `email`, `ip`, `userAgent`, `country`, `locale`, and `appVersion`. You can also pass any key-value pairs in an object or dictionary to the `custom` field to create targeting based on them.
After the user logs in or their attributes change, call `updateUser` with the updated `userID` and any other updated user attributes:
{% codetabs %}
```java Java
StatsigUser newUser = new StatsigUser("new_user_id");
Statsig.updateUserAsync(newUser, this); // this must implement IStatsigCallback
...
@Override
public void onStatsigUpdateUser() {
// User has been updated and values have been refetched for the new user
}
```
```kotlin Kotlin
Statsig.updateUser(StatsigUser("new_user_id"))
```
{% /codetabs %}
### Private attributes
To prevent sensitive user PII from being logged, use the `privateAttributes` field on the StatsigUser object. Statsig uses any attribute set in `privateAttributes` only for evaluation and targeting, and removes it from logs before sending them to the server.
For example, if you have feature gates that should only pass for users with emails ending in "@statsig.com" but don't want to log email addresses to Statsig, add the key-value pair `{ email: "my_user@statsig.com" }` to `privateAttributes` on the user.
## Statsig Options
Pass an optional `options` parameter in addition to `sdkKey` and `user` during initialization to customize the Statsig client.
{% parameter name="api" type="String" %}
Default endpoint for all SDK network requests. Do not override unless you implement the Statsig API elsewhere.
{% /parameter %}
{% parameter name="disableCurrentActivityLogging" type="Boolean" %}
Include the current top-level activity on logged events by default. Set to `true` to disable.
{% /parameter %}
{% parameter name="disableDiagnosticsLogging" type="Boolean" %}
Deprecated. Previously prevented the SDK from sending diagnostic information.
{% /parameter %}
{% parameter name="initTimeoutMs" type="Long" %}
Milliseconds to wait for the initial request before completing. Set to `0` to wait indefinitely.
{% /parameter %}
{% parameter name="enableAutoValueUpdate" type="Boolean" %}
Periodically fetch updated values for the current user when enabled.
{% /parameter %}
{% parameter name="autoValueUpdateIntervalMinutes" type="Double" %}
Frequency (in minutes) for auto value refresh. Minimum is `1` minute.
{% /parameter %}
{% parameter name="overrideStableID" type="String?" %}
Override the SDK-generated `stableID` for the user.
{% /parameter %}
{% parameter name="loadCacheAsync" type="Boolean" %}
Whether the SDK should block on loading saved values from disk.
{% /parameter %}
{% parameter name="initializeValues" type="Map?" %}
Provide the initialize response directly to bootstrap the client synchronously. Go to the NodeJS Server SDK for generating values and the Bootstrap docs.
{% /parameter %}
{% parameter name="disableHashing" type="Boolean?" %}
When `true`, gate/config/experiment names aren't hashed and remain readable. Requires special authorization from Statsig.
{% /parameter %}
{% parameter name="customCacheKey" type="((sdkKey: String, user: StatsigUser) -> String)" %}
Override how the cache key is generated for stored values when the default doesn't fit your needs.
{% /parameter %}
{% parameter name="evaluationCallback" type="((config: BaseConfig) -> Unit)" %}
Callback invoked whenever a gate, config, experiment, or layer is checked. Receives the evaluated `BaseConfig`.
{% /parameter %}
{% parameter name="lifetimeCallback" type="IStatsigLifetimeCallback?" %}
Callbacks that may trigger multiple times over the lifetime of the client SDK.
The SDK calls them on the main thread.
* `onValuesUpdated()` - called whenever the fired when new values are received and available for use. May be called after `Statsig.updateUser()`, `Statsig.updateUserAsync()`, or auto value updates (see `enableAutoValueUpdate` above)
{% /parameter %}
#### Methods
* **setTier | setEnvironmentParameter | getEnvironment**
* used to signal the environment tier the user is currently in.
* `setTier` can be PRODUCTION, STAGING or DEVELOPMENT. For example, passing in a value of `Tier.STAGING` allows your users to pass any condition that passes for the staging environment tier, and fail any condition that only passes for other environment tiers.
* `setEnvironmentParameter` can be used for custom tiers, for example `options.setEnvironmentParameter("tier", "test")`
#### Runtime options
Starting in `V4.43.0`, a subset of options can be set during initialization and later updated while the Statsig client is running.
These options are defined in `StatsigRuntimeMutableOptions` (which `StatsigOptions` extends) and are detailed below.
Call `Statsig.updateRuntimeOptions(runtimeMutableOptions: StatsigRuntimeMutableOptions)` or the corresponding method in `StatsigClient` to update the Statsig client with new values.
* **loggingEnabled**: `Boolean`, default `true`
* Setting this value to `false` prevents the Statsig client from sending logging events over the network or saving events to its on-disk cache. The 1000 most recent events are queued in memory. You can log them to network (or cache them) by setting `loggingEnabled` to `true` later during that session.
* Calling `Statsig.flush()` after setting `loggingEnabled` to `true` immediately clears the queue and minimizes loss of older log events.
* This is useful for cases where users must grant permission before events should be logged, or in any other cases where logging shouldn't be enabled.
## Shutting Statsig Down
To save data and battery usage and prevent logged events from being dropped, the SDK keeps event logs in client cache and flushes them periodically. Because of this, some events may not have been sent when your app shuts down.
To ensure all logged events are flushed or saved locally, call `shutdown` when your app is closing:
{% codetabs %}
```java Java
Statsig.shutdown();
```
```kotlin Kotlin
Statsig.shutdown()
```
{% /codetabs %}
## Using persistent evaluations
To ensure that a user's variant stays consistent while an experiment is running, regardless of changes to allocation or targeting, use persistent storage. The Android SDK supports a minimal implementation using the keepDeviceValues flag. Refer to the [Client Persistent Assignment Doc](/client/concepts/persistent_assignment#support-in-ios-and-android-sdks) for more information.
## Local overrides
To locally override gates/configs/experiments/layers for testing, Statsig offers convenient methods for a quick local override. Unless you call the remove method, the SDK persists these session-to-session on the client's device. These overrides apply locally only and don't affect definitions in the console or elsewhere.
```kotlin
// Overrides the given gate to the specified value
overrideGate(gateName: String, value: Boolean)
// Overrides the given config (dynamic config or experiment) to the provided value
overrideConfig(configName: String, value: Map)
// Removes any overrides associated with the provided gate/config/experiment name
removeOverride(name: String)
// Removes all overrides
removeAllOverrides()
// Returns the set of gate and config overrides currently in place on the client
getAllOverrides(): StatsigOverrides
class StatsigOverrides(
@SerializedName("gates")
val gates: MutableMap,
@SerializedName("configs")
val configs: MutableMap>
) {}
```
## Manual exposures
{% callout type="warning" %}
Manual logging is error-prone and can introduce issues like uneven exposures, which compromise experiment results.
{% /callout %}
You can query your gates/experiments without triggering an exposure, and manually log the exposures later:
{% tabs %}
{% tab title="Check Gate" %}
To check a gate without an exposure being logged:
{% codetabs %}
```kotlin Kotlin
val result = Statsig.checkGateWithExposureLoggingDisabled("a_gate_name")
```
```java Java
boolean result = Statsig.checkGateWithExposureLoggingDisabled("a_gate_name");
```
{% /codetabs %}
Later, to manually log the gate exposure:
{% codetabs %}
```kotlin Kotlin
Statsig.manuallyLogGateExposure("a_gate_name")
```
```java Java
Statsig.manuallyLogGateExposure("a_gate_name");
```
{% /codetabs %}
{% /tab %}
{% tab title="Get Config" %}
To get a dynamic config without an exposure being logged:
{% codetabs %}
```kotlin Kotlin
val config = Statsig.getConfigWithExposureLoggingDisabled("a_config_name")
```
```java Java
DynamicConfig config = Statsig.getConfigWithExposureLoggingDisabled("a_config_name");
```
{% /codetabs %}
Later, to manually log the config exposure:
{% codetabs %}
```kotlin Kotlin
Statsig.manuallyLogConfigExposure("a_config_name")
```
```java Java
Statsig.manuallyLogConfigExposure("a_config_name");
```
{% /codetabs %}
{% /tab %}
{% tab title="Get Experiment" %}
To get an experiment without an exposure being logged:
{% codetabs %}
```kotlin Kotlin
val experiment = Statsig.getExperimentWithExposureLoggingDisabled("an_experiment_name")
```
```java Java
DynamicConfig experiment = Statsig.getExperimentWithExposureLoggingDisabled("an_experiment_name");
```
{% /codetabs %}
Later, to manually log the experiment exposure:
{% codetabs %}
```kotlin Kotlin
Statsig.manuallyLogExperimentExposure("an_experiment_name", false)
```
```java Java
Statsig.manuallyLogExperimentExposure("an_experiment_name", false);
```
{% /codetabs %}
{% /tab %}
{% tab title="Get Layer" %}
To get a layer parameter without an exposure being logged:
{% codetabs %}
```kotlin Kotlin
val layer = Statsig.getLayerWithExposureLoggingDisabled("a_layer_name", false)
val result = layer.getString("a_parameter_name", "fallback")
```
```java Java
Layer layer = Statsig.getLayerWithExposureLoggingDisabled("a_layer_name");
String result = layer.getString("a_parameter_name", "fallback");
```
{% /codetabs %}
Later, to manually log the layer parameter exposure:
{% codetabs %}
```kotlin Kotlin
Statsig.manuallyLogLayerParameterExposure("a_layer_name", "a_parameter_name", false)
```
```java Java
Statsig.manuallyLogLayerParameterExposure("a_layer_name", "a_parameter_name", false);
```
{% /codetabs %}
{% /tab %}
{% /tabs %}
## StableID
Each client SDK has a stableID: a device-level identifier generated the first time the SDK is initialized and stored locally for all future sessions. The stableID doesn't change unless storage is wiped or the app is deleted. This enables device-level experiments and experiments where other user-identifiable information is unavailable, such as for logged-out users.
```kotlin
// Retrieve the StableID
Statsig.getStableID();
// Override the StableID before initializing, if you have something you'd prefer to use instead
val opts = StatsigOptions(overrideStableID = "my_stable_id")
Statsig.initialize(app, "client-xyx", options = opts)
```
## Using multiple instances of the SDK
The examples above use the SDK's singleton. Multiple instances of the SDK are also supported. The `Statsig` singleton wraps a single instance of the SDK (typically called a `StatsigClient`) that you can instantiate directly.
{% callout type="note" %}
You must use a different SDK key for each SDK instance you create for this to work. Various functionality of the Statsig client is keyed on the SDK key being used. Using the same key leads to collisions.
{% /callout %}
All top-level static methods from the singleton carry over as instance methods. To create an instance of the Statsig SDK:
{% codetabs %}
```java Java
StatsigClient client = new StatsigClient();
client.initializeAsync(application, sdkKey, user, callback, options);
```
```kotlin Kotlin
var client: StatsigClient = StatsigClient()
client.initialize(application, sdkKey, user, options)
```
{% /codetabs %}
## Initialize response
The SDK provides a method to access the raw values used internally for gate, config, and layer evaluation. This is useful for debugging or advanced use cases where you need to access the underlying data. For example, you can use these values to bootstrap another SDK, such as the JavaScript SDK when opening an in-app browser.
The `getInitializeResponseJson` method returns an `ExternalInitializeResponse` object that contains:
1. A JSON string representation of the initialize response values
2. Evaluation details that provide metadata about how the values were obtained (network, cache, etc.)
{% codetabs %}
```java Java
// Get the raw values that the SDK is using internally to provide gate/config/layer results
ExternalInitializeResponse response = Statsig.getInitializeResponseJson();
// Get the JSON string representation of the initialize response
String jsonValues = response.getInitializeResponseJSON();
// Get the evaluation details
EvaluationDetails details = response.getEvaluationDetails();
```
```kotlin Kotlin
// Get the raw values that the SDK is using internally to provide gate/config/layer results
val response = Statsig.getInitializeResponseJson()
// Get the JSON string representation of the initialize response
val jsonValues = response.getInitializeResponseJSON()
// Get the evaluation details
val details = response.getEvaluationDetails()
```
{% /codetabs %}
================================================================================
# iOS/tvOS/macOS Client SDK
URL: https://docs.statsig.com/client/iosClientSDK
Updated: 2026-07-11
================================================================================
## Set up the SDK
{% steps %}
{% step title="Install the SDK" %}
To use the SDK in your project, add Statsig as a dependency.
{% tabs %}
{% tab title="Swift Package Manager" %}
In your Xcode, select File > Swift Packages > Add Package Dependency
and enter the URL https://github.com/statsig-io/statsig-kit.git.
You can also include it directly in your project's Package.swift. Find the latest release version on the [GitHub page](https://github.com/statsig-io/statsig-kit/releases).
```swift
//...
dependencies: [
// see the latest version on https://github.com/statsig-io/statsig-kit/releases
.package(url: "https://github.com/statsig-io/statsig-kit.git", .upToNextMinor("X.Y.Z")),
],
//...
targets: [
.target(
name: "YOUR_TARGET",
dependencies: ["Statsig"]
)
],
//...
```
{% /tab %}
{% tab title="Cocoapods" %}
If you use CocoaPods, the pod name is 'Statsig'. Include the following line in your Podfile:
```ruby
use_frameworks!
target 'TargetName' do
//...
pod 'Statsig', '~> X.Y.Z'
end
```
Find the latest versions by searching [cocoapods.org](https://cocoapods.org/) or on [Github](https://github.com/statsig-io/statsig-kit/releases).
{% /tab %}
{% /tabs %}
{% /step %}
{% step title="Initialize the SDK" %}
Initialize the SDK with a client SDK key from the ["API Keys" tab on the Statsig console](https://console.statsig.com/api_keys). These keys are safe to embed in a client application.
Along with the key, pass in a [User Object](#statsig-user) with the attributes you want to target later in a gate or experiment.
{% codetabs %}
```swift Swift
Statsig.initialize(
sdkKey: "my_client_sdk_key",
user: StatsigUser(userID: "my_user_id"),
options: StatsigOptions(environment: StatsigEnvironment(tier: .Staging)))
{ error in
// Statsig has finished fetching the latest feature gate and experiment values for your user.
// If you need the most recent values, you can get them now.
// You can also check error.message and error.code for any debugging information.
}
```
```objective-c Objective C
StatsigUser *user = [[StatsigUser alloc] initWithUserID:@"my_user_id"];
[Statsig initializeWithSDKKey:@"my_client_sdk_key" user:user completion:^(StatsigClientError * error) {
// Statsig has finished fetching the latest feature gate and experiment values for your user.
// If you need the most recent values, you can get them now.
// You can also check error.message and error.code for any debugging information.
}];
```
{% /codetabs %}
The SDK calls the completion block after the network request to fetch the latest feature gate and experiment values for your user.
If you request any value before the SDK calls the completion block, you may get the cached value from the previous session or the default value. Wait for the completion block before requesting the latest value.
{% /step %}
{% /steps %}
## Use the SDK
### Checking a Feature Flag/Gate
Now that your SDK is initialized, check a [**Feature Gate**](/feature-flags/overview). Feature Gates create logic branches in code that can be rolled out to different users from the Statsig Console. Gates are always **CLOSED** or **OFF** (think `return false;`) by default.
{% codetabs %}
```swift Swift
if Statsig.checkGate("new_homepage_design") {
// Gate is on, show new home page
} else {
// Gate is off, show old home page
}
```
```objective-c Objective C
if ([Statsig checkGateForName:@"new_homepage_design"]) {
// Gate is on, show new home page
} else {
// Gate is off, show old home page
}
```
{% /codetabs %}
### Reading a Dynamic Config
Feature Gates work well for simple on/off switches with optional advanced user targeting. To send different values (strings, numbers, and similar types) to your clients based on specific user attributes such as country, use **Dynamic Configs**. The API is similar to Feature Gates but returns a full JSON object you can configure on the server and fetch typed parameters from. For example:
{% codetabs %}
```swift Swift
let config = Statsig.getConfig("awesome_product_details")
// The 2nd parameter is the default value to be used in case the given parameter name does not exist on
// the Dynamic Config object. This can happen when there is a typo, or when the user is offline and the
// value has not been cached on the client.
let itemName = config.getValue(forKey: "product_name", defaultValue: "Awesome Product v1")
let price = config.getValue(forKey: "price", defaultValue: 10.0)
let shouldDiscount = config.getValue(forKey: "discount", defaultValue: false)
```
```objective-c Objective C
DynamicConfig *config = [Statsig getConfigForName:@"awesome_product_details"];
// The 2nd parameter is the default value to be used in case the given parameter name does not exist on
// the Dynamic Config object. This can happen when there is a typo, or when the user is offline and the
// value has not been cached on the client.
NSString *itemName = [config.getStringForKey:@"product_name" defaultValue:@"Awesome Product v1"];
double price = [config getDoubleForKey:@"price" defaultValue:10.0];
BOOL shouldDiscount = [config getBoolForKey:@"discount" defaultValue:false];
```
{% /codetabs %}
### Getting a Layer/Experiment
Use **Layers/Experiments** to run A/B/n experiments. Statsig offers two APIs, but recommends [layers](/experiments/layers-overview) to enable quicker iterations with parameter reuse.
{% codetabs %}
```swift Swift
// Values via getLayer
let layer = Statsig.getLayer("user_promo_experiments")
let promoTitle = layer.getValue(forKey: "title", defaultValue: "Welcome to Statsig!")
let discount = layer.getValue(forKey: "discount", defaultValue: 0.1)
// or, via getExperiment
let titleExperiment = Statsig.getExperiment("new_user_promo_title")
let priceExperiment = Statsig.getExperiment("new_user_promo_price")
let promoTitle = titleExperiment.getValue(forKey: "title", defaultValue: "Welcome to Statsig")
let discount = priceExperiment.getValue(forKey: "discount", defaultValue: 0.1)
...
let price = msrp * (1 - discount);
```
```objective-c Objective C
DynamicConfig *expConfig = [Statsig getExperimentForName:@"new_user_promo"];
NSString *promoTitle = [expConfig.getStringForKey:@"title" defaultValue:@"Welcome to Statsig! Use discount code WELCOME10OFF for 10% off your first purchase!"];
double discount = [expConfig getDoubleForKey:@"discount" defaultValue:0.1];
double price = msrp * (1 - discount);
```
{% /codetabs %}
### Logging an Event
After you set up a Feature Gate or an Experiment, you can track custom events to measure how your new features or experiment groups affect those events. Call the Log Event API for the event, and optionally provide a value and/or a metadata object to log together with the event:
{% codetabs %}
```swift Swift
Statsig.logEvent(withName: "purchase", value: 2.99, metadata: ["item_name": "remove_ads"])
```
```objective-c Objective C
[Statsig logEvent:@"purchase" doubleValue:2.99 metadata:@{ @"item_name" : @"remove_ads" }];
```
{% /codetabs %}
## Parameter Stores
Parameter Stores hold a set of parameters for your mobile app. These parameters can be remapped dynamically from a static value to a Statsig entity (Feature Gates, Experiments, and Layers), so you can decouple your code from the configuration in Statsig. Go to [Parameter Stores](/client/concepts/parameter-stores) to learn more.
## Statsig User
Provide a StatsigUser object to check or get your configurations. Pass as much information as possible to take advantage of advanced gate and config conditions.
The `userID` field is usually required to provide a consistent experience for a given user. (Refer to [logged-out experiments](/guides/first-device-level-experiment) to understand how to correctly run experiments for logged-out users.)
Besides `userID`, the `email`, `ip`, `userAgent`, `country`, `locale`, and `appVersion` fields are also available as top-level fields on StatsigUser. You can also pass any key-value pairs in an object/dictionary to the `custom` field to create targeting based on them.
After the user logs in or their attributes change, call `updateUserWithResult` with the updated `userID` and/or any other updated user attributes.
{% codetabs %}
```swift Swift
let user = StatsigUser(
userID: "a-user-id",
email: "user@example.com",
ip: "192.168.1.1",
userAgent: "Mozilla/5.0",
country: "US",
locale: "en_US",
appVersion: "1.0.0",
custom: [
"plan": "premium",
"age": 25
],
customIDs: [
"stableID": "stable-id-123"
],
privateAttributes: [
"email": "private@example.com"
]
)
```
```objective-c Objective C
StatsigUser *user = [[StatsigUser alloc] initWithUserID:@"a-user-id"];
user.email = @"user@example.com";
user.ip = @"192.168.1.1";
user.userAgent = @"Mozilla/5.0";
user.country = @"US";
user.locale = @"en_US";
user.appVersion = @"1.0.0";
user.custom = @{ @"plan": @"premium", @"age": @25 };
user.customIDs = @{ @"stableID": @"stable-id-123" };
user.privateAttributes = @{ @"email": @"private@example.com" };
```
{% /codetabs %}
## Statsig Options
{% parameter name="initTimeout" type="Double" %}
Determines how long the Statsig client waits for the initial network request to respond before calling the completion block. The Statsig client returns either cached values (if any) or default values if checkGate/getConfig/getExperiment is called before the initial network request completes.
If you always want to wait for the latest values fetched from Statsig server, set this to 0 to avoid timing out the network request.
{% /parameter %}
{% parameter name="disableCurrentVCLogging" type="Bool" %}
By default, any custom event your application logs with `Statsig.logEvent()` includes the current root View Controller. This allows Statsig to generate user journey funnels for your users. Set this parameter to `true` to disable this behavior.
{% /parameter %}
{% parameter name="printHandler" type="((String) -> Void)?" %}
A handler for log messages from the SDK. If not provided, the SDK prints logs to the console.
The handler receives the message string that would otherwise be printed to the console.
Useful for redirecting logs to your own logging system, suppressing unnecessary console output, or debugging issues with the SDK.
{% /parameter %}
{% parameter name="environment" type="StatsigEnvironment" %}
StatsigEnvironment is a class for setting environment variables that apply to all of your users in the same session. Statsig uses these variables for targeting purposes.
For example, passing in a value of `StatsigEnvironment(tier: .Staging)` allows your users to pass any condition that passes for the staging environment tier, and fail any condition that only passes for other environment tiers.
{% /parameter %}
{% parameter name="evaluationCallback" type="EvaluationCallbackData" %}
EvaluationCallback provides a callback when an evaluation occurs against one of your configurations (gate, dynamic config, experiment, layer, and parameter stores). This is useful when you want to trigger specific actions or log evaluations based on the results received from Statsig.
To use the EvaluationCallback, provide a callback function during SDK initialization through StatsigOptions. The SDK invokes the callback every time an evaluation occurs for feature gates, dynamic configs, experiments, layers, or parameter stores.
The EvaluationCallbackData enum defines the different types of data that the evaluationCallback returns when the Statsig iOS SDK evaluates feature gates, dynamic configs, experiments, layers, or parameter stores.
Here is the structure of the enum:
```swift
public enum EvaluationCallbackData {
case gate (FeatureGate)
case config (DynamicConfig)
case experiment (DynamicConfig)
case layer (Layer)
case parameterStore (ParameterStore)
}
```
Here's an example of how to set up an evaluation callback:
```swift
func callback(data: StatsigOptions.EvaluationCallbackData) {
switch data {
case .gate(let gate):
// Handle gate evaluation
case .config(let config):
// Handle dynamic config evaluation
case .experiment(let experiment):
// Handle experiment evaluation
case .layer(let layer):
// Handle layer evaluation
case .parameterStore(let paramStore):
// Handle parameter store evaluation
}
}
let opts = StatsigOptions(evaluationCallback: callback)
Statsig.initialize(sdkKey: "client-key", options: opts)
```
{% /parameter %}
{% parameter name="storageProvider" type="StorageProvider" %}
Lets you implement a custom caching strategy by passing an object that conforms to the `StorageProvider` protocol.
Default cache key: `com.statsig.cache`
```swift
@objc public protocol StorageProvider {
@objc func read(_ key: String) -> Data?
@objc func write(_ value: Data, _ key: String)
@objc func remove(_ key: String)
}
```
{% /parameter %}
{% parameter name="overrideStableID" type="String" %}
Overrides the auto generated StableID that is set for the device.
{% /parameter %}
{% parameter name="enableCacheByFile" type="Bool" %}
Use file caching instead of UserDefaults. Useful if you run into size limits with UserDefaults (ie tvOS).
{% /parameter %}
{% parameter name="eventLoggingEnabled" type="Bool" %}
Controls whether the SDK sends events over the network. Useful when user consent is needed before sending events. The iOS SDK stores up to 1MB of unsent request payloads.
{% /parameter %}
{% parameter name="initializeValues" type="[String: Any]" %}
Provide a Dictionary representing the "initialize response" required to synchronously initialize the SDK.
Obtain this value from a Statsig server SDK and use it to [Bootstrap](/client/concepts/initialize/#2-bootstrap-initialization) the SDK when initializing.
{% /parameter %}
{% parameter name="disableDiagnostics" type="Bool" %}
Prevent the SDK from sending useful debug information to Statsig.
{% /parameter %}
{% parameter name="disableHashing" type="Bool" %}
When disabled, the SDK doesn't hash gate/config/experiment names, and they remain readable as plain text.
{% callout type="info" %}
This requires special authorization from Statsig. Reach out to the support team, your sales contact, or through the [Slack community](https://statsig.com/slack) if you want this enabled.
{% /callout %}
{% /parameter %}
{% parameter name="shutdownOnBackground" type="Bool" %}
The SDK automatically shuts down when an app enters the background.
If you need to use the SDK while your app is in the background, set this to `false`.
{% /parameter %}
{% parameter name="initializationURL" type="URL" %}
Override the URL used to initialize the SDK. Learn more at /custom_proxy
```swift
StatsigOptions(initializationURL: URL(string: "https://example.com/setup"))
```
{% /parameter %}
{% parameter name="eventLoggingURL" type="URL" %}
Override the URL used to log events. Learn more at /custom_proxy
```swift
StatsigOptions(eventLoggingURL: URL(string: "https://example.com/info"))
```
{% /parameter %}
## StableID
Each client SDK has the concept of stableID: a device-level identifier generated the first time the SDK is initialized and stored locally for all future sessions. Unless storage is wiped (or the app is deleted), the stableID doesn't change.
This allows Statsig to run device-level experiments and experiments when other user-identifiable information is unavailable (logged-out users).
{% codetabs %}
```swift Swift
let options = StatsigOptions(overrideStableID: "my_stable_id")
Statsig.initialize(sdkKey: "client-xyz", options: options)
```
```objective-c Objective C
StatsigOptions *options = [[StatsigOptions alloc] init];
options.overrideStableID = @"my_stable_id";
[Statsig initializeWithSDKKey:@"client-xyz" user:nil options:options completion:nil];
```
{% /codetabs %}
## Manual Exposures
{% callout type="warning" %}
Manual logging is error-prone and can often introduce issues like uneven exposures, which compromise experiment results.
{% /callout %}
You can query your gates/experiments without triggering an exposure and manually log the exposures later.
{% tabs %}
{% tab title="Feature Gates" %}
{% codetabs %}
```swift Swift
// Swift - Check without logging exposure
let result = Statsig.checkGateWithExposureLoggingDisabled("a_gate_name")
// ...
// Later, when ready to log the exposure
Statsig.manuallyLogGateExposure("a_gate_name")
```
```objc Objective-C
// Objective C - Check without logging exposure
bool result = [Statsig checkGateWithExposureLoggingDisabled:@"a_gate_name"];
// ...
// Later, when ready to log the exposure
[Statsig manuallyLogGateExposure:@"a_gate_name"];
```
{% /codetabs %}
{% /tab %}
{% tab title="Dynamic Configs" %}
{% codetabs %}
```swift Swift
// Swift - Get config without logging exposure
let config = Statsig.getConfigWithExposureLoggingDisabled("a_config_name")
// ...
// Later, when ready to log the exposure
Statsig.manuallyLogConfigExposure("a_config_name")
```
```objc Objective-C
// Objective C - Get config without logging exposure
DynamicConfig *config = [Statsig getConfigWithExposureLoggingDisabled:@"a_config_name"];
// ...
// Later, when ready to log the exposure
[Statsig manuallyLogConfigExposure:@"a_config_name"];
```
{% /codetabs %}
{% /tab %}
{% tab title="Experiments" %}
{% codetabs %}
```swift Swift
// Swift - Get experiment without logging exposure
let experiment = Statsig.getExperimentWithExposureLoggingDisabled("an_experiment_name")
// ...
// Later, when ready to log the exposure
Statsig.manuallyLogExperimentExposure("an_experiment_name")
```
```objc Objective-C
// Objective C - Get experiment without logging exposure
DynamicConfig *experiment = [Statsig getExperimentWithExposureLoggingDisabled:@"an_experiment_name"];
// ...
// Later, when ready to log the exposure
[Statsig manuallyLogExperimentExposure:@"an_experiment_name"];
```
{% /codetabs %}
{% /tab %}
{% tab title="Layers" %}
{% codetabs %}
```swift Swift
// Swift - Get layer without logging exposure
let layer = Statsig.getLayerWithExposureLoggingDisabled("a_layer_name")
let result = layer.getValue(forKey: "a_parameter_name", defaultValue: "fallback")
// ...
// Later, when ready to log the exposure
Statsig.manuallyLogLayerParameterExposure("a_layer_name", "a_parameter_name")
```
```objc Objective-C
// Objective C - Get layer without logging exposure
Layer *layer = [Statsig getLayerWithExposureLoggingDisabled:@"a_layer_name"];
NSString *result = [layer getStringForKey:@"a_parameter_name" defaultValue:@"fallback"];
// ...
// Later, when ready to log the exposure
[Statsig manuallyLogLayerParameterExposure:@"a_layer_name" parameterName:@"a_parameter_name"];
```
{% /codetabs %}
{% /tab %}
{% /tabs %}
## Local Overrides
To locally override gates/configs/experiments/layers for testing, use the local override methods. Unless you call the remove method, these overrides persist session-to-session on the client's device. These overrides apply locally only and don't affect definitions in the console or elsewhere.
## Shutting Statsig Down
To save users' data and battery usage and prevent logged events from being dropped, the SDK keeps event logs in client cache and flushes them periodically. Because of this, some events may not have been sent when your app shuts down.
To ensure all logged events are flushed or saved locally, call shutdown when your app is closing.
{% codetabs %}
```swift Swift
Statsig.shutdown()
```
```objective-c Objective C
[Statsig shutdown];
```
{% /codetabs %}
## Using multiple instances of the SDK
The examples above use the SDK's singleton. Statsig also supports creating multiple instances of the SDK. The `Statsig` singleton wraps a single instance of the SDK (typically called a `StatsigClient`) that you can instantiate directly.
{% callout type="note" %}
Use a different SDK key for each SDK instance. Various functionality of the Statsig client is keyed on the SDK key being used. Using the same key causes collisions.
{% /callout %}
All top-level static methods from the singleton carry over as instance methods. To create an instance of the Statsig SDK:
{% codetabs %}
```swift Swift
let client = StatsigClient(
sdkKey: "client-xyz",
user: StatsigUser(userID: "user-1"),
options: StatsigOptions(environment: StatsigEnvironment(tier: .Production))
) { error in
// ready
}
let gateOn = client.checkGate("some_gate")
```
```objective-c Objective C
StatsigOptions *options = [[StatsigOptions alloc] initWithEnvironment:[[StatsigEnvironment alloc] initWithTier:StatsigEnvironmentTierProduction]];
StatsigUser *user = [[StatsigUser alloc] initWithUserID:@"user-1"];
StatsigClient *client = [[StatsigClient alloc] initWithSdkKey:@"client-xyz" user:user options:options completion:^(StatsigClientError * error) {
// ready
}];
BOOL gateOn = [client checkGate:@"some_gate"];
```
{% /codetabs %}
{% callout type="warning" %}
Use a unique SDK key per instance to avoid collisions.
{% /callout %}
## Initialize Response
The SDK provides a method to access the raw values used internally for gate, config, and layer values. This is useful for debugging or for advanced use cases where you need to access the underlying data. For example, you can use these values to bootstrap another SDK, such as the JavaScript SDK when you open an in-app browser.
The `getInitializeResponseJson` method returns an `ExternalInitializeResponse` object that contains:
1. A JSON string representation of the initialize response values
2. Evaluation details that provide metadata about how the values were obtained (network, cache, etc.)
{% codetabs %}
```swift Swift
let response = Statsig.getInitializeResponseJson()
if let values = response.values {
print(values)
}
let details = response.evaluationDetails
```
```objective-c Objective C
ExternalInitializeResponse *response = [Statsig getInitializeResponseJson];
NSString *values = response.values;
if (values) {
NSLog(@"%@", values);
}
EvaluationDetails *details = response.evaluationDetails;
```
{% /codetabs %}
## Listening for changes
In v1.14.0+, you can listen for SDK changes using `StatsigListening`.
{% codetabs %}
```swift Swift
class MyViewController: UIViewController, StatsigListening {
override func viewDidLoad() {
super.viewDidLoad()
if Statsig.isInitialized() {
render()
} else {
Statsig.addListener(self)
renderLoading()
}
}
private func render() {
let showNewUI = Statsig.checkGate("new_ui_enabled")
if showNewUI {
// Render the new UI
} else {
// Render the old UI
}
}
private func renderLoading() { /* loading UI */ }
private func renderError(error: StatsigClientError) { /* error UI */ }
// StatsigListening
func onInitializedWithResult(_ error: StatsigClientError?) {
if let error = error {
renderError(error)
return
}
render()
}
func onUserUpdatedWithResult(_ error: StatsigClientError?) { /* optional rerender */ }
}
```
{% /codetabs %}
================================================================================
# C++ Client SDK
URL: https://docs.statsig.com/client/CPP
Updated: 2026-07-11
================================================================================
## Set Up the SDK
{% steps %}
{% step title="Install the SDK" %}
{% tabs %}
{% tab title="Downloaded From GitHub" %}
```cpp
add_subdirectory(path/to/downloaded/statsig_sdk)
target_link_libraries(${PROJECT_NAME} StatsigClientSDK)
```
{% /tab %}
{% tab title="FetchContent" %}
```cpp
include(FetchContent)
FetchContent_Declare(statsig
GIT_REPOSITORY https://github.com/statsig-io/cpp-client-sdk.git
GIT_TAG main
)
FetchContent_MakeAvailable(statsig)
target_link_libraries(${PROJECT_NAME} StatsigClientSDK)
```
{% /tab %}
{% /tabs %}
{% /step %}
{% step title="Initialize the SDK" %}
Next, initialize the SDK with a client SDK key from the ["API Keys" tab on the Statsig console](https://console.statsig.com/api_keys). These keys are safe to embed in a client application.
Along with the key, pass in a [User Object](#statsig-user) with the attributes you'd like to target later on in a gate or experiment.
```cpp
#include
using namespace statsig;
StatsigUser user;
user.user_id = "a-user";
user.custom_ids = {
{"employeeID", "an-employee"}
};
// Create your own instance
StatsigClient client;
// Initialize synchronously using cached values from the previous session
client.InitializeSync("client-{YOUR_CLIENT_SDK_KEY}", user);
// or, Initialize asynchronously from network
client.InitializeAsync(
"client-{YOUR_CLIENT_SDK_KEY}",
[](StatsigResultCode result) {
// completion callback
},
user
);
```
**Synchronous** initialization uses cache (if available) and returns immediately. Data for subsequent sessions is then fetched in the background.
**Asynchronous** initialization provides a callback, allowing you to wait for the most current data to be fetched.
For convenience, there is also a singleton instance accessible through `StatsigClient::Shared()`.
```cpp
// Initialize synchronously using cached values from the previous session
StatsigClient::Shared().InitializeSync("client-{YOUR_CLIENT_SDK_KEY}", user);
// or, Initialize asynchronously from network
StatsigClient::Shared().InitializeAsync(
"client-{YOUR_CLIENT_SDK_KEY}",
[](StatsigResultCode result) {
// completion callback
},
user
);
```
**Optional** - Configuration through StatsigOptions
To adjust how the SDK works, pass a [StatsigOptions](#statsig-options) struct during initialization.
```cpp
StatsigOptions options;
options.environment = StatsigEnvironment{"staging"};
client.InitializeSync(..., options);
// or
client.InitializeAsync(..., options);
```
{% /step %}
{% /steps %}
## Use the SDK
### Checking a Feature Flag/Gate
After the SDK is initialized, check a [**Feature Gate**](/feature-flags/overview). Feature Gates create logic branches in code that can be rolled out to different users from the Statsig Console. Gates are always **CLOSED** or **OFF** (equivalent to `return false;`) by default.
```cpp
if (client.CheckGate("a_gate")) {
// show new feature
}
// or, use the shared instance
if (StatsigClient::Shared().CheckGate("a_gate")) {
// show new feature
}
```
### Reading a Dynamic Config
Feature Gates work well for simple on/off switches with optional advanced user targeting. To send a different set of values (strings, numbers, and so on) to clients based on specific user attributes such as country, use **Dynamic Configs**. The API is similar to Feature Gates, but returns an entire JSON object you configure on the server, from which you can fetch typed parameters. For example:
```cpp
DynamicConfig config = client.GetDynamicConfig("a_config");
// or, use the shared instance
DynamicConfig config = StatsigClient::Shared().GetDynamicConfig("a_config");
// then access the params
std::cout << config.GetValue()["a_string_param"] << std::endl;
```
{% callout type="info" %}
`DynamicConfig.GetValue` returns `JsonObj`, which is an unordered map of string to `nlohmann/json`. Refer to https://github.com/nlohmann/json
{% /callout %}
### Getting a Layer/Experiment
**Layers/Experiments** support running A/B/n experiments. Two APIs are available, but [layers](/experiments/layers-overview) are recommended to enable quicker iterations with parameter reuse.
```cpp
// Values via getLayer
Layer layer = StatsigClient::Shared().GetLayer("name");
std::string promoTitle = layer.GetValue("title").get();
double discount = layer.GetValue("discount").get();
// or, via getExperiment
Experiment titleExperiment = StatsigClient::Shared().GetExperiment("new_user_promo_title");
Experiment priceExperiment = StatsigClient::Shared().GetExperiment("new_user_promo_price");
std::string promoTitle = titleExperiment.GetValue()["title"].get();
double discount = priceExperiment.GetValue()["discount"].get();
```
{% callout type="info" %}
`Layer.GetValue` and `Experiment.GetValue` return `JsonObj`, which are unordered maps of string to `nlohmann/json`. Refer to https://github.com/nlohmann/json
{% /callout %}
### Logging an Event
After setting up a Feature Gate or an Experiment, you can track custom events to see how your new features or different experiment groups affect those events. Call the Log Event API for the event, and optionally provide a value and metadata object to be logged with the event:
```cpp
std::unordered_map metadata{
{ "price", "9.99" },
{ "item_name", "some_great_product" }
};
StatsigEvent event("add_to_cart", "SKU_12345", metadata);
StatsigClient::Shared().LogEvent(event);
// Then, at some point later, you need to "flush" the events
StatsigClient::Shared().Flush();
```
## Statsig User
You need to provide a StatsigUser object to check or get your configurations. Pass as much information as possible to take advantage of advanced gate and config conditions.
The `userID` field is required in most cases to provide a consistent experience for a given user (refer to [logged-out experiments](/guides/first-device-level-experiment) for how to run experiments for logged-out users).
In addition to `userID`, the following top-level fields are available on StatsigUser: `email`, `ip`, `userAgent`, `country`, `locale`, and `appVersion`. You can also pass any key-value pairs in an object or dictionary to the `custom` field to create targeting based on them.
```cpp
StatsigUser user;
user.user_id = "a-user";
user.email = "developer@statsig.com";
user.custom_ids = {
{"employeeID", "an-employee"}
};
```
### Private attributes
To prevent sensitive user PII from being logged, use the `privateAttributes` field on the StatsigUser object. Any attribute set in `privateAttributes` is used only for evaluation and targeting, and Statsig removes it from logs before sending them to the server.
For example, if you have feature gates that should only pass for users with emails ending in "@statsig.com" but don't want to log email addresses to Statsig, add the key-value pair `{ email: "my_user@statsig.com" }` to `privateAttributes` on the user.
### Updating users
When user data changes, call an UpdateUser function to make Statsig aware of the new user.
```cpp
client.UpdateUserSync(user);
// or, use the shared instance
StatsigClient::Shared().UpdateUserSync(user);
```
To ensure you have the latest values during an update (for example, when transitioning from logged out to logged in), use the asynchronous update function.
```cpp
{client or StatsigClient::Shared()}.UpdateUserAsync(
user,
[](StatsigResultCode result) {
if (result == StatsigResultCode::Ok) {
// do something now that the latest values have been fetched
} else {
// error state
}
}
);
```
Asynchronous vs Synchronous behaviors are the same as the Initialize functions.
## Statsig Options
`StatsigClient::Initialize`, in addition to `sdk_key` and `user`, takes an optional `options` parameter to customize the StatsigClient. Current options are listed below:
{% parameter name="api" type="std::string" %}
The API to use for all SDK network requests. You don't need to override this unless you have another API that implements the Statsig API endpoints.
{% /parameter %}
{% parameter name="providers" type="EvaluationsDataProvider" %}
Array of EvaluationsDataProvider, used to customize the initialization and update behavior.
{% /parameter %}
## Shutting Statsig Down
To save data and battery usage and prevent logged events from being dropped, the SDK keeps event logs in client cache and flushes them periodically. Because of this, some events may not have been sent when your app shuts down.
To ensure all logged events are flushed or saved locally, call `Shutdown` when your app is closing:
```cpp
client.Shutdown();
// or, use the shared instance
StatsigClient::Shared().Shutdown();
```
#### How do I run experiments for logged-out users?
Go to the guide on [device level experiments](/guides/first-device-level-experiment).
================================================================================
# Dart Client SDK
URL: https://docs.statsig.com/client/Dart
Updated: 2026-07-11
================================================================================
## Set Up the SDK
{% steps %}
{% step title="Install the SDK" %}
With Dart:
```bash
dart pub add statsig
```
With Flutter:
```bash
flutter pub add statsig
```
{% callout type="note" %}
If you are using **Flutter**, be sure to add Statsig as part of the [app lifecycle](#flutter-lifecycle-hooks) to avoid losing events.
{% /callout %}
{% /step %}
{% step title="Initialize the SDK" %}
Next, initialize the SDK with a client SDK key from the ["API Keys" tab on the Statsig console](https://console.statsig.com/api_keys). These keys are safe to embed in a client application.
Along with the key, pass in a [User Object](#statsig-user) with the attributes you'd like to target later on in a gate or experiment.
```dart
import 'package:statsig/statsig.dart';
await Statsig.initialize('client-sdk-key', StatsigUser(userId: "a-user-id"));
```
{% /step %}
{% /steps %}
## Use the SDK
### Checking a Feature Flag/Gate
After the SDK is initialized, check a [**Feature Gate**](/feature-flags/overview). Feature Gates create logic branches in code that can be rolled out to different users from the Statsig Console. Gates are always **CLOSED** or **OFF** (equivalent to `return false;`) by default.
```dart
if (Statsig.checkGate("new_homepage_design")) {
// Gate is on, show new home page
} else {
// Gate is off, show old home page
}
```
### Reading a Dynamic Config
Feature Gates work well for simple on/off switches with optional advanced user targeting. To send a different set of values (strings, numbers, and so on) to clients based on specific user attributes such as country, use **Dynamic Configs**. The API is similar to Feature Gates, but returns an entire JSON object you configure on the server, from which you can fetch typed parameters. For example:
```dart
var config = Statsig.getConfig("awesome_product_details");
// The 2nd parameter is the default value to be used in case the given parameter name does not exist on
// the Dynamic Config object. This can happen when there is a typo, or when the user is offline and the
// value has not been cached on the client.
var itemName = config.get("product_name", "Awesome Product v1");
var price = config.get("price", 10.0);
var shouldDiscount = config.get("discount", false);
```
### Getting a Layer/Experiment
**Layers/Experiments** support running A/B/n experiments. Two APIs are available, but [layers](/experiments/layers-overview) are recommended to enable quicker iterations with parameter reuse.
```dart
// Values via getLayer
var layer = Statsig.getLayer("user_promo_experiments");
var promoTitle = layer.getString("title", "Welcome to Statsig!");
var discount = layer.getDouble("discount", 0.1);
// or, via getExperiment
var titleExperiment = Statsig.getExperiment("new_user_promo_title");
var priceExperiment = Statsig.getExperiment("new_user_promo_price");
var promoTitle = titleExperiment.get("title", "Welcome to Statsig!");
var discount = priceExperiment.get("discount", 0.1);
```
### Logging an Event
After setting up a Feature Gate or an Experiment, you can track custom events to see how your new features or different experiment groups affect those events. Call the Log Event API for the event, and optionally provide a value and metadata object to be logged with the event:
```dart
// Provide a doubleValue argument for number values
Statsig.logEvent("purchase", doubleValue: 2.99, metadata: {"item_name": "remove_ads"});
// or provide a stringValue argument for string values
Statsig.logEvent("login", stringValue: "a.user@mail.com");
```
## Parameter Stores
Parameter Stores hold a set of parameters for your mobile app. These parameters can be remapped dynamically from a static value to a Statsig entity (Feature Gates, Experiments, and Layers), so you can decouple your code from the configuration in Statsig. Refer to [Param Stores](/client/concepts/parameter-stores) for more information.
```dart
var homepageStore = Statsig.getParameterStore("homepage");
var title = homepageStore.get("title", "Welcome");
var shouldShowUpsell = homepageStore.get("upsell_upgrade_now", false);
```
## Statsig User
You need to provide a StatsigUser object to check or get your configurations. Pass as much information as possible to take advantage of advanced gate and config conditions.
The `userID` field is required in most cases to provide a consistent experience for a given user (refer to [logged-out experiments](/guides/first-device-level-experiment) for how to run experiments for logged-out users).
In addition to `userID`, the following top-level fields are available on StatsigUser: `email`, `ip`, `userAgent`, `country`, `locale`, and `appVersion`. You can also pass any key-value pairs in an object or dictionary to the `custom` field to create targeting based on them.
After the user logs in or their attributes change, call `updateUser` with the updated `userID` and any other updated user attributes:
```dart
await Statsig.updateUser(StatsigUser("a_new_user"));
```
### Private attributes
To prevent sensitive user PII from being logged, use the `privateAttributes` field on the StatsigUser object. Statsig uses any attribute set in `privateAttributes` only for evaluation and targeting, and removes it from logs before sending them to the server.
For example, if you have feature gates that should only pass for users with emails ending in "@statsig.com" but don't want to log email addresses to Statsig, add the key-value pair `{ email: "my_user@statsig.com" }` to `privateAttributes` on the user.
## Statsig Options
{% parameter name="initTimeout" type="int" %}
Used to decide how long (in seconds) the Statsig client waits for the initial network request to respond before calling the completion block. The Statsig client returns either cached values (if any) or default values if `checkGate`/`getConfig`/`getExperiment` is called before the initial network request completes.
If you always want to wait for the latest values fetched from the Statsig server, set this to `0` so the network request doesn't time out.
{% /parameter %}
{% parameter name="api" type="string" %}
The endpoint to use for all SDK network requests. Don't override this unless you have another API that implements the Statsig API endpoints.
{% /parameter %}
{% parameter name="environment" type="string" %}
The environment tier to evaluate rules for the current user. Default is empty, which is the same as "production." On non-production tiers, events don't make it into downstream pulse results.
{% /parameter %}
## Lifecycle and advanced usage
## Shutting Statsig Down
To save data and battery usage and prevent logged events from being dropped, the SDK keeps event logs in client cache and flushes them periodically. Because of this, some events may not have been sent when your app shuts down.
To ensure all logged events are flushed or saved locally, call `shutdown` when your app is closing:
```dart
await Statsig.shutdown();
```
## Flutter lifecycle hooks
Mobile apps can be closed by the operating system when they're no longer in the foreground. To ensure all events are logged before the operating system closes the app, add Statsig to the app lifecycle events. This flushes all pending events when an app state change is detected.
Something like the following:
```dart
// An example App Lifecycle Observer
class StatsigLifecycleObserver extends WidgetsBindingObserver {
@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
switch (state) {
case AppLifecycleState.resumed:
await Statsig.initialize('client-sdk-key');
break;
case AppLifecycleState.paused:
await Statsig.shutdown();
break;
}
}
}
```
Then in your app code, add this observer to the WidgetsBinding instance.
```dart
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(StatsigLifecycleObserver());
}
```
## FAQs
### How do I run experiments for logged out users?
Go to the guide on [device level experiments](/client/concepts/persistent_assignment).
================================================================================
# .NET Client SDK
URL: https://docs.statsig.com/client/DotNet
Updated: 2026-07-11
================================================================================
## Set up the SDK
{% steps %}
{% step title="Install the SDK" %}
The package is hosted on [Nuget](https://www.nuget.org/packages/Statsig/). Install it from your Visual Studio's Nuget package manager, or through the NuGet CLI:
```shell
nuget install Statsig
```
{% /step %}
{% step title="Initialize the SDK" %}
Next, initialize the SDK with a client SDK key from the ["API Keys" tab on the Statsig console](https://console.statsig.com/api_keys). These keys are safe to embed in a client application.
Along with the key, pass in a [User Object](#statsig-user) with the attributes you'd like to target later on in a gate or experiment.
```csharp
using Statsig;
using Statsig.Client;
await StatsigClient.Initialize(
"client-sdk-key",
new StatsigUser { UserID = "some_user_id", Email = "user@email.com" },
new StatsigOptions(new StatsigEnvironment(EnvironmentTier.Development)) // optional, use when needed to customize certain behaviors
);
```
{% /step %}
{% /steps %}
## Use the SDK
### Checking a Feature Flag/Gate
Now that your SDK is initialized, let's check a [**Feature Gate**](/feature-flags/overview). Feature Gates can be used to create logic branches in code that can be rolled out to different users from the Statsig Console. Gates are always **CLOSED** or **OFF** (think `return false;`) by default.
```csharp
if (StatsigClient.CheckGate("new_homepage_design"))
{
// Gate is on, show new home page
}
else
{
// Gate is off, show old home page
}
```
### Reading a Dynamic Config
Feature Gates are useful for simple on/off switches with optional advanced user targeting. To send a different set of values (strings, numbers, etc.) to clients based on specific user attributes such as country, use **Dynamic Configs**. The API is similar to Feature Gates, but you get a complete JSON object you can configure on the server and fetch typed parameters from it. For example:
```csharp
DynamicConfig config = StatsigClient.GetConfig("awesome_product_details");
// The 2nd parameter is the default value to be used in case the given parameter name does not exist on
// the Dynamic Config object. This can happen when there is a typo, or when the user is offline and the
// value has not been cached on the client.
string itemName = config.Get("product_name", "Awesome Product v1");
double price = config.Get("price", 10.0);
bool shouldDiscount = config.Get("discount", false);
```
### Getting a Layer/Experiment
**Layers/Experiments** let you run A/B/n experiments. Two APIs are available, but Statsig recommends [layers](/experiments/layers-overview) for quicker iterations with parameter reuse.
```csharp
// Values via getLayer
Layer layer = StatsigClient.GetLayer("user_promo_experiments");
var promoTitle = layer.Get("title", "Welcome to Statsig!");
var discount = layer.Get("discount", 0.1);
// or, via getExperiment
DynamicConfig titleExperiment = StatsigClient.GetExperiment("new_user_promo_title");
DynamicConfig priceExperiment = StatsigClient.GetExperiment("new_user_promo_price");
var promoTitle = titleExperiment.Get("title", "Welcome to Statsig!");
var discount = priceExperiment.Get("discount", 0.1);
...
double price = msrp * (1 - discount);
```
### Logging an Event
After setting up a Feature Gate or Experiment, you may want to track custom events to see how new features or experiment groups affect those events. Call the Log Event API for the event. You can also provide a value and metadata object to be logged with the event:
```csharp
StatsigClient.LogEvent(
"add_to_cart",
"SKU_12345",
new Dictionary() {
{ "price", "9.99" },
{ "item_name", "diet_coke_48_pack" }
}
);
```
## Statsig User
You need to provide a StatsigUser object to check/get your configurations. Pass as much
information as possible to take advantage of advanced gate and config conditions.
Most of the time, the `userID` field is needed to provide a consistent experience for a given
user (refer to [logged-out experiments](/guides/first-device-level-experiment) to understand how to correctly run experiments for logged-out
users).
Besides `userID`, Statsig also supports `email`, `ip`, `userAgent`, `country`, `locale` and `appVersion` as top-level fields on
StatsigUser. In addition, you can pass any key-value pairs in an object/dictionary to the `custom` field and create targeting based on them.
After the user logs in or their attributes change, call `updateUser` with the updated `userID` and any other updated user attributes:
```csharp
// if you want to update the existing user, or change to a different user, call updateUser
await StatsigClient.UpdateUser(
new StatsigUser { UserID = "new_user_id", Email = "new_user@email.com" },
);
```
### Private Attributes
To prevent sensitive user PII from being logged, use the `privateAttributes` field on the StatsigUser object. Statsig uses attributes set in `privateAttributes` only for evaluation and targeting, then removes them from any logs before sending them to the server.
For example, if you have feature gates that pass only for users with emails ending in "@statsig.com" but don't want to log your users' email addresses to Statsig, add the key-value pair `{ email: "my_user@statsig.com" }` to `privateAttributes` on the user.
## Statsig Options
`Initialize()` takes an optional parameter `options` in addition to `sdkKey` and `user` that you can provide to customize the Statsig client.
{% parameter name="environment" type="StatsigEnvironment" %}
Set environment variables that apply to all users in the session for targeting purposes.
Commonly used to set the environment tier, e.g. `new StatsigEnvironment(EnvironmentTier.Staging)`.
{% /parameter %}
{% parameter name="ClientRequestTimeoutMs" type="int" %}
Maximum milliseconds to wait for `/initialize` before proceeding with cached/default values.
{% /parameter %}
{% parameter name="PersistentStorageFolder" type="string" %}
Directory path for persistent storage of cached values and logs.
{% /parameter %}
## Shutting Statsig Down
The SDK keeps event logs in the client cache and flushes them periodically to save data and battery usage. Because of this, some events may not have been flushed when your app shuts down.
To ensure all logged events are flushed or saved locally, shut down Statsig when your app is closing:
```csharp
StatsigClient.Shutdown();
```
## FAQs
#### How do I run experiments for logged out users?
Refer to [Device-level experiments](/sdks/user#device-level-experiments).
================================================================================
# Expo Client SDK
URL: https://docs.statsig.com/client/Expo
Updated: 2026-07-11
================================================================================
## Set up the SDK
{% steps %}
{% step title="Install the SDK" %}
```shell
npx expo install @statsig/expo-bindings
```
### Peer Dependencies
The `@statsig/expo-bindings` package has peer dependencies which may also need to be installed if they aren't already in your project.
```shell
npx expo install expo-device expo-application @react-native-async-storage/async-storage
```
{% /step %}
{% step title="Initialize the SDK" %}
Next, initialize the SDK with a client SDK key from the ["API Keys" tab on the Statsig console](https://console.statsig.com/api_keys). These keys are safe to embed in a client application.
Along with the key, pass in a [User Object](#statsig-user) with the attributes you'd like to target later on in a gate or experiment.
The setup for an Expo environment is very similar to a plain [React environment](/client/React).
The only difference is that you need to use the Expo specific `StatsigProviderExpo`.
This automatically switches out the storage layer used by the SDK, utilizing [AsyncStorage](https://github.com/react-native-async-storage) instead of LocalStorage (which isn't available in RN environments).
```tsx
import { StatsigProviderExpo, useFeatureGate } from "@statsig/expo-bindings";
function Content() {
const gate = useFeatureGate("a_gate");
return Reason: {gate.details.reason}; // Reason: Network or NetworkNotModified
}
function App() {
return (
Loading...}
>
);
}
```
{% /step %}
{% /steps %}
## Use the SDK
You can get an instance of the StatsigClient to check gates, experiments, dynamic configs, layers, and log events.
```jsx
import { useStatsigClient } from "@statsig/expo-bindings";
const { client } = useStatsigClient();
```
The methods you can call on the client are listed below.
### Checking a Feature Flag/Gate
Now that your SDK is initialized, let's check a [**Feature Gate**](/feature-flags/overview). Feature Gates can be used to create logic branches in code that can be rolled out to different users from the Statsig Console. Gates are always **CLOSED** or **OFF** (think `return false;`) by default.
You can evaluate a gate by getting the client with the `useStatsigClient` hook, and then calling `checkGate`
```tsx
const { client } = useStatsigClient();
return (
Gate is {client.checkGate('check_user') ? 'passing' : 'failing'}.
);
```
### Reading a Dynamic Config
Feature Gates are useful for simple on/off switches with optional advanced user targeting. To send a different set of values (strings, numbers, etc.) to clients based on specific user attributes such as country, use **Dynamic Configs**. The API is similar to Feature Gates, but you get a complete JSON object you can configure on the server and fetch typed parameters from it. For example:
You can get a DynamicConfig value by getting the client with the `useStatsigClient` hook, and then calling `getConfig`
```tsx
const { client } = useStatsigClient();
const config = client.getConfig('app_properties');
return (
{config.get('title', 'Default Title')}
);
```
### Getting a Layer/Experiment
**Layers/Experiments** let you run A/B/n experiments. Two APIs are available, but Statsig recommends [layers](/experiments/layers-overview) for quicker iterations with parameter reuse.
You can access the experiment variant and parameters for the user by getting the client with the `useStatsigClient` hook, and then calling `getExperiment`.
```tsx
const { client } = useStatsigClient();
const experiment = client.getExperiment('headline_test');
return (
);
```
You can access layers and layer parameters for the user by getting the client with the `useStatsigClient` hook, and then calling `getLayer`.
```tsx
const { client } = useStatsigClient();
const layer = client.getLayer('homepage_layer');
return (
);
```
### Logging an Event
After setting up a Feature Gate or Experiment, you may want to track custom events to see how new features or experiment groups affect those events. Call the Log Event API for the event. You can also provide a value and metadata object to be logged with the event.
Get the client with the `useStatsigClient` hook, then call `logEvent`:
```tsx
const { client } = useStatsigClient();
return
```
### Flushing Logged Events
`flush()` sends queued events immediately. Use `shutdown()` when your app is exiting.
```tsx
import { Button } from 'react-native';
import { useStatsigClient } from '@statsig/expo-bindings';
const { client } = useStatsigClient();
return (
);
```
## Loading State
Depending on your setup, you may want to wait for the latest values before checking a gate or experiment. If you are using `StatsigProviderExpo`, pass in a `loadingComponent` prop to display a loading state while the SDK is initializing. If you are using the `useClientAsyncInitExpo` hook, check the `isLoading` prop to determine if the SDK is still loading.
{% tabs %}
{% tab title="StatsigProviderExpo" %}
```tsx
export function App() {
const loadingComponent =
;
}
return (
);
}
```
{% /tab %}
{% /tabs %}
## Lifecycle & Advanced Usage
## Shutting Statsig Down
The SDK keeps event logs in the client cache and flushes them periodically to save data and battery usage. Because of this, the SDK may not have flushed some events when your app shuts down.
To ensure all logged events are flushed or saved locally, shut down Statsig when your app is closing:
```tsx
import { useEffect } from 'react';
import { useStatsigClient } from '@statsig/expo-bindings';
const { client } = useStatsigClient();
useEffect(() => {
return () => {
void client.shutdown();
};
}, [client]);
```
## Advanced
### Expo Without React
In some scenarios, you may need to use the `StatsigClient` outside the React component tree. For example, in background tasks or notification handlers, use the Expo-specific `StatsigClientExpo`.
```tsx
import { StatsigClientExpo } from '@statsig/expo-bindings';
const myClient = new StatsigClientExpo(
YOUR_CLIENT_KEY,
{ userID: "a-user" }
);
await myClient.initializeAsync();
if (myClient.checkGate("my_gate")) {
// do something cool
}
```
### Synchronous Storage with MMKV
If you are using [MMKV](https://github.com/mrousavy/react-native-mmkv) in your project and prefer it over the default ([AsyncStorage](https://github.com/react-native-async-storage)), provide your own `StorageProvider` through `StatsigOptions`.
```tsx
import { MMKV } from "react-native-mmkv";
import { StorageProvider } from "@statsig/client-core";
import { StatsigProviderExpo } from "@statsig/expo-bindings";
function App() {
const [storageProvider] = useState(() => {
const mmkv = new MMKV();
return {
isReady: () => true,
isReadyResolver: () => null,
getProviderName: () => "MMKV",
getAllKeys: () => mmkv.getAllKeys(),
getItem: (key: string) => mmkv.getString(key) ?? null,
setItem: (key: string, value: string) => mmkv.set(key, value),
removeItem: (key: string) => mmkv.delete(key),
};
});
return (
...
);
}
```
## Debugging
### Network Issues
Some users have reported `ERROR: A networking error occured during POST request` messages when first initializing Statsig. Statsig resolved this issue in SDK versions after 3.1.0. Upgrade your SDK to resolve the error. If the error persists, reach out through [Slack](https://statsig.com/slack).
================================================================================
# React Native Client SDK
URL: https://docs.statsig.com/client/ReactNative
Updated: 2026-07-11
================================================================================
## Set up the SDK
{% steps %}
{% step title="Install the SDK" %}
## Installation
Statsig uses a multi-package strategy. Install both the Statsig client and the React Native specific bindings.
{% codetabs %}
```shell NPM
npm install @statsig/react-native-bindings
```
```shell Yarn
yarn add @statsig/react-native-bindings
```
{% /codetabs %}
### Peer Dependencies
The `@statsig/react-native-bindings` package has peer dependencies which may also need to be installed if they aren't already in your project.
{% codetabs %}
```shell NPM
npm install react-native-device-info @react-native-async-storage/async-storage
```
```shell Yarn
yarn add react-native-device-info @react-native-async-storage/async-storage
```
{% /codetabs %}
{% /step %}
{% step title="Initialize the SDK" %}
Next, initialize the SDK with a client SDK key from the ["API Keys" tab on the Statsig console](https://console.statsig.com/api_keys). These keys are safe to embed in a client application.
Along with the key, pass in a [User Object](#statsig-user) with the attributes you'd like to target later on in a gate or experiment.
## React Native + React Specific Setup
The setup for a ReactNative environment is very similar to a plain [React environment](/client/React).
The only difference is that you need to use the ReactNative specific `StatsigProviderRN`.
This automatically switches out the storage layer used by the SDK, utilizing [AsyncStorage](https://github.com/react-native-async-storage) instead of LocalStorage (which isn't available in RN environments).
```tsx
import {
StatsigProviderRN,
useFeatureGate,
} from "@statsig/react-native-bindings";
function Content() {
const gate = useFeatureGate("a_gate");
// Reason: Network or NetworkNotModified
return (
Value: {gate.value ? "Pass" : "Fail"}Reason: {gate.details.reason}
);
}
function App() {
return (
Loading...}
>
);
}
```
{% /step %}
{% /steps %}
## Use the SDK
Get an instance of the StatsigClient to check gates, experiments, dynamic configs, layers, and log events.
```jsx
import { useStatsigClient } from "@statsig/react-native-bindings";
const { client } = useStatsigClient();
```
The available methods are listed in the sections below.
### Checking a Feature Flag/Gate
Now that your SDK is initialized, let's check a [**Feature Gate**](/feature-flags/overview). Feature Gates can be used to create logic branches in code that can be rolled out to different users from the Statsig Console. Gates are always **CLOSED** or **OFF** (think `return false;`) by default.
You can evaluate a gate by getting the client with the `useStatsigClient` hook, and then calling `checkGate`
```tsx
const { client } = useStatsigClient();
return (
Gate is {client.checkGate('check_user') ? 'passing' : 'failing'}.
);
```
### Reading a Dynamic Config
Feature Gates are useful for simple on/off switches with optional advanced user targeting. To send a different set of values (strings, numbers, etc.) to clients based on specific user attributes such as country, use **Dynamic Configs**. The API is similar to Feature Gates, but you get a complete JSON object you can configure on the server and fetch typed parameters from it. For example:
You can get a DynamicConfig value by getting the client with the `useStatsigClient` hook, and then calling `getConfig`
```tsx
const { client } = useStatsigClient();
const config = client.getConfig('app_properties');
return (
{config.get('title', 'Default Title')}
);
```
### Getting a Layer/Experiment
**Layers/Experiments** let you run A/B/n experiments. Two APIs are available, but Statsig recommends [layers](/experiments/layers-overview) for quicker iterations with parameter reuse.
You can access the experiment variant and parameters for the user by getting the client with the `useStatsigClient` hook, and then calling `getExperiment`.
```tsx
const { client } = useStatsigClient();
const experiment = client.getExperiment('headline_test');
return (
);
```
You can access layers and layer parameters for the user by getting the client with the `useStatsigClient` hook, and then calling `getLayer`.
```tsx
const { client } = useStatsigClient();
const layer = client.getLayer('homepage_layer');
return (
);
```
### Logging an Event
After setting up a Feature Gate or Experiment, you may want to track custom events to see how new features or different experiment groups affect those events. Call the Log Event API for the event. You can also provide a value and metadata object to be logged with the event.
Get the client with the `useStatsigClient` hook, then call `logEvent`:
```tsx
const { client } = useStatsigClient();
return
```
### Flushing Logged Events
`flush()` sends queued events immediately. Use `shutdown()` when your app is exiting.
```tsx
import { Button } from 'react-native';
import { useStatsigClient } from '@statsig/react-native-bindings';
const { client } = useStatsigClient();
return (