Registers a controlled experiment AND puts its assignment flag live, in that…
Registers a controlled experiment AND puts its assignment flag live, in that order, so the arms start bucketing subjects the moment this returns 201 — the…
POST /v1/experiment
| Address | https://api.hanzo.ai/v1/experiment |
| Method | POST |
| Operation | post_experiment |
| Auth | Authorization: Bearer $HANZO_API_KEY |
Registers a controlled experiment AND puts its assignment flag live, in that order, so the arms start bucketing subjects the moment this returns 201 — the flag is created active at 100% rollout, with each variant weighted as declared. There is no separate start call; creating IS starting.
A variant carries an opaque payload this primitive never interprets: a feature config, an ad-creative id, a subject line, a model id.
Requires a validated principal, and refuses without one. The org and project are taken from that principal and the creator is stamped from the credential — none of the three is a body field, so an experiment cannot be filed against another tenant. An id already used in this project is a conflict, never a silent overwrite: re-creating would stomp the assignment flag of a run in progress.
It fails closed on the flag write. An experiment whose assignment flag does not exist would assign nobody, so if that write fails nothing is registered.
Request
11 fields, body application/json (required).
| Field | In | Type | Required | Description |
|---|---|---|---|---|
exposureEvent | body | string | — | ExposureEvent is the event that marks a subject as enrolled — the analysis denominator — defaulting to the SDK's $feature_flag_called marker. |
flagKey | body | string | — | FlagKey names the assignment flag this experiment writes, defaulting to exp_<id>. |
id | body | string | yes | ID is the experiment's slug, claimed once per project. |
metricEvent | body | string | yes | MetricEvent is the event that counts as a conversion — the analysis numerator. |
name | body | string | — | Name is free text for a reader; the id is what addresses the experiment. |
subjectKind | body | string | — | SubjectKind is the unit assigned and measured: user (the default), org, session or audience. |
variants | body | Arm[] | yes | Arms are the arms, at least two. |
variants[].control | body | boolean | — | true on the baseline arm every other arm is compared to |
variants[].key | body | string | — | the arm's slug, unique within the experiment |
variants[].payload | body | any | — | opaque JSON the arm carries, which the consumer interprets |
variants[].weight | body | number | — | its share of the rollout; the arms sum to 100 |
Response
| Status | Body | Meaning |
|---|---|---|
201 | Trial | created |
201 body — 18 fields.
| Field | In | Type | Always | Description |
|---|---|---|---|---|
createdAt | body | string | — | when it started assigning |
createdBy | body | string | — | the credential that registered it |
decidedAt | body | string | — | when the promotion took effect |
decidedBy | body | string | — | the credential that promoted the winner |
exposureEvent | body | string | — | the event that enrols a subject — the analysis denominator |
flagKey | body | string | — | the assignment flag this experiment drives |
id | body | string | — | the experiment's slug, unique within the project |
metricEvent | body | string | — | the event that counts as a conversion — the numerator |
name | body | string | — | free text for a reader |
project | body | string | — | the sub-scope within the org, stamped from the principal |
status | body | string | — | running while it assigns and measures, decided once a winner is promoted |
subjectKind | body | string | — | the unit assigned and measured: user, org, session or audience |
variants | body | Arm[] | — | the arms, weighted, one of them the control |
variants[].control | body | boolean | — | true on the baseline arm every other arm is compared to |
variants[].key | body | string | — | the arm's slug, unique within the experiment |
variants[].payload | body | any | — | opaque JSON the arm carries, which the consumer interprets |
variants[].weight | body | number | — | its share of the rollout; the arms sum to 100 |
winner | body | string | — | the arm promoted to the whole rollout |
Failure carries the platform error shape — see Errors.
Examples
hanzo experiments create \
--id <id> \
--metric-event <metricEvent> \
--variants '[{"control":false,"key":"<key>","payload":"<payload>","weight":0}]'import { Configuration, ExperimentApi } from 'hanzoai';
const api = new ExperimentApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.postExperiment({ id: "<id>", metricEvent: "<metricEvent>", variants: [{"control":false,"key":"<key>","payload":"<payload>","weight":0}] });from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import ExperimentApi
client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = ExperimentApi(client).post_experiment(id="<id>", metric_event="<metricEvent>", variants=[{"control":false,"key":"<key>","payload":"<payload>","weight":0}])cfg := cloud.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := cloud.NewAPIClient(cfg)
resp, _, err := client.ExperimentAPI.PostExperiment(context.Background()).Execute()
if err != nil {
return err
}use hanzo_cloud::apis::{configuration::Configuration, experiment_api};
let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();
let result = experiment_api::post_experiment(&cfg, Default::default()).await?;import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.ExperimentApi;
ApiClient client = new ApiClient();
client.setRequestInterceptor(b -> b.header("Authorization", "Bearer " + System.getenv("HANZO_API_KEY")));
var result = new ExperimentApi(client).postExperiment();The method above is the one at the current release of the document. [email protected] (npm) and [email protected] (PyPI) were generated from an earlier release, where this operation carried a different id, so it spells the method differently — regenerating the clients is what makes the two agree. SDKs →
curl -X POST https://api.hanzo.ai/v1/experiment \
-H "Authorization: Bearer $HANZO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"id": "<id>",
"metricEvent": "<metricEvent>",
"variants": [
{
"control": false,
"key": "<key>",
"payload": "<payload>",
"weight": 0
}
]
}'The door reaches experiment through the experiments tool, which names its 7 operations with its own verbs — this one among them, under a name only the door declares. describe explains any of them:
curl -X POST https://api.hanzo.ai/v1/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "describe",
"arguments": {
"op": "list_experiments"
}
}
}'How is this guide?