Skip to main content

Documentation Index

Fetch the complete documentation index at: https://auth0-feat-testing-docs.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Experiment Center is currently in Beta. This feature is available to select design partners only and is not supported for production use. To learn more about Auth0’s product release cycle, read Product Release Stages.
This guide walks you through creating a percentage-based A/B experiment using the Management API. By the end, you will have an active experiment that assigns 90% of traffic to control and 10% to a treatment variation.

Prerequisites

  • A Management API token with the following scopes: create:experimentation, read:experimentation, update:experimentation, delete:experimentation
  • Your tenant must be enabled for the Experiment Center Beta (contact your Auth0 account team)

Base URL

All Experiment Center endpoints are under:
https://{yourDomain}/api/v2/experimentation/

Step 1: Create a feature flag

A feature flag defines the feature you are testing. It contains typed configuration parameters with default values (the baseline state).
curl --request POST \
  --url 'https://{yourDomain}/api/v2/experimentation/feature-flags' \
  --header 'authorization: Bearer {managementApiToken}' \
  --header 'content-type: application/json' \
  --data '{
    "name": "Passkey Enrollment Prompt",
    "parameters": {
      "show_passkey_prompt": {
        "type": "boolean",
        "value": false,
        "description": "Whether to show the passkey enrollment prompt"
      },
      "prompt_style": {
        "type": "string",
        "value": "none",
        "description": "Visual style for the prompt (none, modal, inline)"
      }
    }
  }'
The response includes the flag’s feature_flag_id (prefixed identifier, e.g., flg_...). Save this value for the following steps.

Step 2: Create variations

Variations represent the different treatments within a feature flag. The control variation uses empty overrides (inherits the baseline). The treatment variation specifies which parameters differ.

Create the control variation

curl --request POST \
  --url 'https://{yourDomain}/api/v2/experimentation/feature-flags/{featureFlagId}/variations' \
  --header 'authorization: Bearer {managementApiToken}' \
  --header 'content-type: application/json' \
  --data '{
    "name": "Control (No Prompt)",
    "overrides": {}
  }'

Create the treatment variation

curl --request POST \
  --url 'https://{yourDomain}/api/v2/experimentation/feature-flags/{featureFlagId}/variations' \
  --header 'authorization: Bearer {managementApiToken}' \
  --header 'content-type: application/json' \
  --data '{
    "name": "Treatment (Passkey Modal)",
    "overrides": {
      "show_passkey_prompt": { "value": true },
      "prompt_style": { "value": "modal" }
    }
  }'
Save both variation_id values from the responses.

Step 3: Activate the feature flag

A feature flag must be in active status before any experiment referencing it can be activated. Activation requires at least 2 variations.
curl --request POST \
  --url 'https://{yourDomain}/api/v2/experimentation/feature-flags/{featureFlagId}/status' \
  --header 'authorization: Bearer {managementApiToken}' \
  --header 'content-type: application/json' \
  --data '{
    "status": "active"
  }'

Step 4: Create the experiment

An experiment references a feature flag and defines how traffic is allocated across variations.
curl --request POST \
  --url 'https://{yourDomain}/api/v2/experimentation/experiments' \
  --header 'authorization: Bearer {managementApiToken}' \
  --header 'content-type: application/json' \
  --data '{
    "name": "Passkey Enrollment Q1",
    "feature_flag_id": "{featureFlagId}",
    "authentication_flow": "pre_login",
    "allocation_strategy": "percentage",
    "allocations": [
      {
        "variation_id": "{controlVariationId}",
        "weight": 90,
        "is_control": true
      },
      {
        "variation_id": "{treatmentVariationId}",
        "weight": 10,
        "is_control": false
      }
    ],
    "assignment_config": {
      "subject": "device"
    }
  }'
Save the experiment_id from the response.
Allocation weights must sum to 100. Exactly one allocation must have is_control: true.

Step 5: Validate the experiment

Before activating, you can check whether the experiment passes all validation rules:
curl --request POST \
  --url 'https://{yourDomain}/api/v2/experimentation/experiments/{experimentId}/validate' \
  --header 'authorization: Bearer {managementApiToken}'
The response includes is_valid (boolean) and an errors array. If is_valid is true, you are ready to activate. Common validation errors:
Error codeCause
experiment_invalid_allocation_weightsWeights do not sum to 100
experiment_missing_controlNo allocation has is_control: true
variation_not_from_flagA variation ID does not belong to the referenced feature flag
feature_flag_not_activeThe referenced feature flag is not in active status
experiment_active_limit_exceededAnother experiment is already active (Beta: one per tenant)

Step 6: Activate the experiment

curl --request POST \
  --url 'https://{yourDomain}/api/v2/experimentation/experiments/{experimentId}/status' \
  --header 'authorization: Bearer {managementApiToken}' \
  --header 'content-type: application/json' \
  --data '{
    "status": "active"
  }'
Once active, the experiment engine resolves the experiment on every authentication transaction and assigns variations using deterministic hashing. The started_at timestamp is set automatically.

Test with query parameters

You can force a specific variation during testing by passing query parameters on the /authorize URL:
https://{yourDomain}/authorize?client_id={clientId}&experiment_id={experimentId}&variation_id={variationId}&...
ParameterDescription
experiment_idForce enrollment in a specific experiment
variation_idForce a specific variation (requires experiment_id)
segment_idForce a specific segment match (requires experiment_id)
Query parameter overrides work for experiments in any status, including draft. This enables a testing workflow where you create an experiment, verify both variants render correctly using query params, and then activate.
Query parameters influence variant assignment only. They do not grant access to experiment data or bypass tenant security policies.

Verify enriched tenant logs

Once the experiment is active and auth transactions are flowing, verify that tenant log events contain experiment metadata:
  1. Navigate to Auth0 Dashboard > Monitoring > Logs or use the Management API logs endpoint.
  2. Look for recent authentication events (successful login, signup).
  3. Confirm the event details include: experiment_id, variant_id, and segment_id.
These enriched events also flow through any log streaming destinations you have configured (Datadog, Splunk, S3, etc.). Pull the data into your analytics tool to compare outcomes across variations.

Use experiment context in your code

Once the experiment is active, experiment context is injected into runtime surfaces automatically.

ACUL components

Enable experiment context on a screen by adding experiment_center to the screen’s context_configuration:
curl --request PATCH \
  --url 'https://{yourDomain}/api/v2/prompts/{prompt}/screen/{screen}/rendering' \
  --header 'authorization: Bearer {managementApiToken}' \
  --header 'content-type: application/json' \
  --data '{
    "context_configuration": ["experiment_center"]
  }'
Then read the context in your ACUL component:
export default function LoginScreen({ experiment_center, ...props }) {
  const showPasskey = experiment_center?.config?.show_passkey_prompt?.value;

  return (
    <div>
      <LoginForm {...props} />
      {showPasskey && <PasskeyEnrollment />}
    </div>
  );
}

Actions

Read experiment context from the event object in post_login, pre_user_registration, or post_user_registration triggers:
exports.onExecutePostLogin = async (event, api) => {
  const ec = event.experiment_center;
  if (!ec?.experiment_id) return;

  const showPasskey = ec.config?.show_passkey_prompt?.value;
  if (showPasskey) {
    api.redirect.sendUserTo("https://example.com/enroll-passkey", {
      query: { experiment_id: ec.experiment_id, variation_id: ec.variation_id }
    });
  }
};

Forms

Pass experiment context to Forms via the vars property in an Action:
exports.onExecutePostLogin = async (event, api) => {
  const ec = event.experiment_center;
  if (ec?.experiment_id) {
    api.prompt.render("my-form-id", {
      vars: {
        variation_id: ec.variation_id,
        show_passkey_prompt: ec.config?.show_passkey_prompt?.value
      }
    });
  }
};
Reference the variables in your Form template using {{vars.variation_id}} or {{vars.show_passkey_prompt}}.

Complete the experiment

When you are ready to conclude the experiment:
curl --request POST \
  --url 'https://{yourDomain}/api/v2/experimentation/experiments/{experimentId}/status' \
  --header 'authorization: Bearer {managementApiToken}' \
  --header 'content-type: application/json' \
  --data '{
    "status": "completed"
  }'
The ended_at timestamp is set automatically. The experiment configuration (including the winning variation’s overrides) is retained so you can reference it while applying the configuration change to your tenant.
Promotion is manual at Beta. After completing the experiment, update your tenant configuration to match the winning variation’s behavior. Automated promotion is a future capability.

Experiment lifecycle reference

StatusDescription
draftCreated but not running. Can be tested via query parameter overrides.
activeRunning. Variant assignment and context injection active for all auth transactions.
pausedTemporarily suspended. No new assignments; in-flight sessions continue.
completedConcluded. Configuration retained for reference during manual promotion.
archivedSoft-deleted via DELETE. Hidden from default list views.
Allowed transitions: draftactive, activepaused, pausedactive, active/pausedcompleted, draft/paused/completedarchived. A completed experiment cannot be reactivated. Create a new experiment to run again.

Learn more