API reference

Experiments

Experiments endpoints.

MethodPathSummary
GET/v1/account/experimentsThe account’s experiments, newest first
POST/v1/account/experimentsPlan an experiment: two or three published versions of one playbook on a number assignment, a route of a number’s schedule or a widget key, a split, a target metric chosen in advance, a minimum sample and an end date
GET/v1/account/experiments/{experimentId}One experiment and its results per arm: every catalogue metric with n and its interval, and verdicts against the control; the target metric’s is labelled tested once every arm has its sample
POST/v1/account/experiments/{experimentId}/startStart splitting traffic. Every arm’s effective playbook for the group is computed first; the start is refused if one needs review
POST/v1/account/experiments/{experimentId}/stopStop an experiment now. Its traffic goes back to the usual version, and its results so far are kept

The account’s experiments, newest first#

GET /v1/account/experiments

Authentication: Bearer token: Authorization: Bearer <token> (Supabase access token). Bearer token: Authorization: Bearer <token> (An API token (ans_pat_ or ans_svc_). Only routes that list a scope here accept one, and only with that scope.).

Query parameters

NameTypeRequiredDescription
statusenumNoOne of: "draft", "running", "stopped", "completed".

Response 200

FieldTypeRequiredDescription
experimentsarray<Experiment>Yes—
experiments[].armsarray<object>Yes—
experiments[].arms[].armenumYesOne of: "A", "B", "C".
experiments[].arms[].playbookVersionIdstring (pbv_… ID)YesID (pbv_…)
experiments[].arms[].versionnumber | nullYes—
experiments[].arms[].weightnumberYes—
experiments[].createdAtstring (date-time)Yes—
experiments[].endReasonenum | nullYesOne of: "ended", "sample_reached", "manual", "new_version_published", "version_archived", "playbook_archived", "scope_changed".
experiments[].endedAtstring (date-time) | nullYes—
experiments[].endsAtstring (date-time)Yes—
experiments[].groupIdstring (grp_… ID)YesID (grp_…)
experiments[].groupNamestring | nullYes—
experiments[].idstring (exp_… ID)YesID (exp_…)
experiments[].minSamplenumberYes—
experiments[].namestringYes—
experiments[].playbookIdstring (pb_… ID)YesID (pb_…)
experiments[].playbookNamestring | nullYes—
experiments[].scopeIdstringYes—
experiments[].scopeKindenumYesOne of: "number_assignment", "number_route", "widget_key".
experiments[].scopeLabelstring | nullYes—
experiments[].startsAtstring (date-time) | nullYes—
experiments[].statusenumYesOne of: "draft", "running", "stopped", "completed".
experiments[].targetMetricenumYesOne of: "target_attainment", "primary_attainment", "reached_rate", "fallback_rate", "opportunity_rate", "attempt_rate", "conversion_after_attempt", "ai_miss_rate", "content_miss_rate", "operations_miss_rate", "caller_miss_rate", "value_per_eligible", "held_rate", "time_to_attempt".

Errors: 400, 401, 403, 404, 409, 429, 500, with an ErrorBody body.

Example

bash
curl -X GET "$ANSWERSTACK_API_URL/v1/account/experiments" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Plan an experiment: two or three published versions of one playbook on a number assignment, a route of a number’s schedule or a widget key, a split, a target metric chosen in advance, a minimum sample and an end date#

POST /v1/account/experiments

Authentication: Bearer token: Authorization: Bearer <token> (Supabase access token).

Request body

application/json, required.

FieldTypeRequiredDescription
armsarray<object>YesUp to 3 items.
arms[].playbookVersionIdstring (pbv_… ID)YesID (pbv_…)
arms[].weightintegerYesBetween 10 and 90.
endsAtstring (date-time)Yes—
groupIdstring (grp_… ID)NoID (grp_…)
minSampleintegerNo—
namestringYes1–120 characters.
playbookIdstring (pb_… ID)YesID (pb_…)
scopeIdstringYes1–64 characters.
scopeKindenumYesOne of: "number_assignment", "number_route", "widget_key".
targetMetricenumYesOne of: "target_attainment", "primary_attainment", "reached_rate", "fallback_rate", "opportunity_rate", "attempt_rate", "conversion_after_attempt", "ai_miss_rate", "content_miss_rate", "operations_miss_rate", "caller_miss_rate", "held_rate".

Response 201

FieldTypeRequiredDescription
armsarray<object>Yes—
arms[].armenumYesOne of: "A", "B", "C".
arms[].playbookVersionIdstring (pbv_… ID)YesID (pbv_…)
arms[].versionnumber | nullYes—
arms[].weightnumberYes—
createdAtstring (date-time)Yes—
endReasonenum | nullYesOne of: "ended", "sample_reached", "manual", "new_version_published", "version_archived", "playbook_archived", "scope_changed".
endedAtstring (date-time) | nullYes—
endsAtstring (date-time)Yes—
groupIdstring (grp_… ID)YesID (grp_…)
groupNamestring | nullYes—
idstring (exp_… ID)YesID (exp_…)
minSamplenumberYes—
namestringYes—
playbookIdstring (pb_… ID)YesID (pb_…)
playbookNamestring | nullYes—
scopeIdstringYes—
scopeKindenumYesOne of: "number_assignment", "number_route", "widget_key".
scopeLabelstring | nullYes—
startsAtstring (date-time) | nullYes—
statusenumYesOne of: "draft", "running", "stopped", "completed".
targetMetricenumYesOne of: "target_attainment", "primary_attainment", "reached_rate", "fallback_rate", "opportunity_rate", "attempt_rate", "conversion_after_attempt", "ai_miss_rate", "content_miss_rate", "operations_miss_rate", "caller_miss_rate", "value_per_eligible", "held_rate", "time_to_attempt".

Errors: 400, 401, 403, 404, 409, 429, 500, with an ErrorBody body.

Example

bash
curl -X POST "$ANSWERSTACK_API_URL/v1/account/experiments" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "string",
  "scopeKind": "number_assignment",
  "scopeId": "string",
  "playbookId": "pb_2ZPh7XbT0v9Ao4iJ3qK1mN8sRgE",
  "arms": [
    {
      "playbookVersionId": "pbv_2ZPh7XbT0v9Ao4iJ3qK1mN8sRgE",
      "weight": 10
    }
  ],
  "targetMetric": "target_attainment",
  "endsAt": "2026-01-15T15:30:00Z"
}'

One experiment and its results per arm: every catalogue metric with n and its interval, and verdicts against the control; the target metric’s is labelled tested once every arm has its sample#

GET /v1/account/experiments/{experimentId}

Authentication: Bearer token: Authorization: Bearer <token> (Supabase access token). Bearer token: Authorization: Bearer <token> (An API token (ans_pat_ or ans_svc_). Only routes that list a scope here accept one, and only with that scope.).

Path parameters

NameTypeRequiredDescription
experimentIdstring (exp_… ID)YesID (exp_…)

Response 200

FieldTypeRequiredDescription
experimentExperimentYes—
experiment.armsarray<object>Yes—
experiment.arms[].armenumYesOne of: "A", "B", "C".
experiment.arms[].playbookVersionIdstring (pbv_… ID)YesID (pbv_…)
experiment.arms[].versionnumber | nullYes—
experiment.arms[].weightnumberYes—
experiment.createdAtstring (date-time)Yes—
experiment.endReasonenum | nullYesOne of: "ended", "sample_reached", "manual", "new_version_published", "version_archived", "playbook_archived", "scope_changed".
experiment.endedAtstring (date-time) | nullYes—
experiment.endsAtstring (date-time)Yes—
experiment.groupIdstring (grp_… ID)YesID (grp_…)
experiment.groupNamestring | nullYes—
experiment.idstring (exp_… ID)YesID (exp_…)
experiment.minSamplenumberYes—
experiment.namestringYes—
experiment.playbookIdstring (pb_… ID)YesID (pb_…)
experiment.playbookNamestring | nullYes—
experiment.scopeIdstringYes—
experiment.scopeKindenumYesOne of: "number_assignment", "number_route", "widget_key".
experiment.scopeLabelstring | nullYes—
experiment.startsAtstring (date-time) | nullYes—
experiment.statusenumYesOne of: "draft", "running", "stopped", "completed".
experiment.targetMetricenumYesOne of: "target_attainment", "primary_attainment", "reached_rate", "fallback_rate", "opportunity_rate", "attempt_rate", "conversion_after_attempt", "ai_miss_rate", "content_miss_rate", "operations_miss_rate", "caller_miss_rate", "value_per_eligible", "held_rate", "time_to_attempt".
resultsExperimentResultsYes—
results.armsarray<object>Yes—
results.arms[].analysednumberYes—
results.arms[].armenumYesOne of: "A", "B", "C".
results.arms[].assignednumberYes—
results.arms[].metricsarray<object>Yes—
results.arms[].metrics[].higherIsBetterbooleanYes—
results.arms[].metrics[].intervalobject | nullYes—
results.arms[].metrics[].knumberYes—
results.arms[].metrics[].labelstringYes—
results.arms[].metrics[].metricenumYesOne of: "target_attainment", "primary_attainment", "reached_rate", "fallback_rate", "opportunity_rate", "attempt_rate", "conversion_after_attempt", "ai_miss_rate", "content_miss_rate", "operations_miss_rate", "caller_miss_rate", "value_per_eligible", "held_rate", "time_to_attempt".
results.arms[].metrics[].nnumberYes—
results.arms[].metrics[].notEnoughbooleanYes—
results.arms[].metrics[].pnumber | nullYes—
results.arms[].metrics[].testedbooleanYes—
results.arms[].metrics[].unitenumYesOne of: "rate", "mean", "seconds".
results.arms[].metrics[].unknownnumberYes—
results.arms[].metrics[].valuenumber | nullYes—
results.arms[].metrics[].verdictenum | nullYesOne of: "better", "worse", "no_clear_difference", "not_enough".
results.arms[].playbookVersionIdstring (pbv_… ID)YesID (pbv_…)
results.arms[].weightnumberYes—
results.minSamplenumberYes—
results.sampleReachedbooleanYes—
results.targetMetricenumYesOne of: "target_attainment", "primary_attainment", "reached_rate", "fallback_rate", "opportunity_rate", "attempt_rate", "conversion_after_attempt", "ai_miss_rate", "content_miss_rate", "operations_miss_rate", "caller_miss_rate", "value_per_eligible", "held_rate", "time_to_attempt".

Errors: 400, 401, 403, 404, 409, 429, 500, with an ErrorBody body.

Example

bash
curl -X GET "$ANSWERSTACK_API_URL/v1/account/experiments/{experimentId}" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Start splitting traffic. Every arm’s effective playbook for the group is computed first; the start is refused if one needs review#

POST /v1/account/experiments/{experimentId}/start

Authentication: Bearer token: Authorization: Bearer <token> (Supabase access token).

Path parameters

NameTypeRequiredDescription
experimentIdstring (exp_… ID)YesID (exp_…)

Response 200

FieldTypeRequiredDescription
armsarray<object>Yes—
arms[].armenumYesOne of: "A", "B", "C".
arms[].playbookVersionIdstring (pbv_… ID)YesID (pbv_…)
arms[].versionnumber | nullYes—
arms[].weightnumberYes—
createdAtstring (date-time)Yes—
endReasonenum | nullYesOne of: "ended", "sample_reached", "manual", "new_version_published", "version_archived", "playbook_archived", "scope_changed".
endedAtstring (date-time) | nullYes—
endsAtstring (date-time)Yes—
groupIdstring (grp_… ID)YesID (grp_…)
groupNamestring | nullYes—
idstring (exp_… ID)YesID (exp_…)
minSamplenumberYes—
namestringYes—
playbookIdstring (pb_… ID)YesID (pb_…)
playbookNamestring | nullYes—
scopeIdstringYes—
scopeKindenumYesOne of: "number_assignment", "number_route", "widget_key".
scopeLabelstring | nullYes—
startsAtstring (date-time) | nullYes—
statusenumYesOne of: "draft", "running", "stopped", "completed".
targetMetricenumYesOne of: "target_attainment", "primary_attainment", "reached_rate", "fallback_rate", "opportunity_rate", "attempt_rate", "conversion_after_attempt", "ai_miss_rate", "content_miss_rate", "operations_miss_rate", "caller_miss_rate", "value_per_eligible", "held_rate", "time_to_attempt".

Errors: 400, 401, 403, 404, 409, 429, 500, with an ErrorBody body.

Example

bash
curl -X POST "$ANSWERSTACK_API_URL/v1/account/experiments/{experimentId}/start" \
  -H "Authorization: Bearer $ACCESS_TOKEN"

Stop an experiment now. Its traffic goes back to the usual version, and its results so far are kept#

POST /v1/account/experiments/{experimentId}/stop

Authentication: Bearer token: Authorization: Bearer <token> (Supabase access token).

Path parameters

NameTypeRequiredDescription
experimentIdstring (exp_… ID)YesID (exp_…)

Response 200

FieldTypeRequiredDescription
armsarray<object>Yes—
arms[].armenumYesOne of: "A", "B", "C".
arms[].playbookVersionIdstring (pbv_… ID)YesID (pbv_…)
arms[].versionnumber | nullYes—
arms[].weightnumberYes—
createdAtstring (date-time)Yes—
endReasonenum | nullYesOne of: "ended", "sample_reached", "manual", "new_version_published", "version_archived", "playbook_archived", "scope_changed".
endedAtstring (date-time) | nullYes—
endsAtstring (date-time)Yes—
groupIdstring (grp_… ID)YesID (grp_…)
groupNamestring | nullYes—
idstring (exp_… ID)YesID (exp_…)
minSamplenumberYes—
namestringYes—
playbookIdstring (pb_… ID)YesID (pb_…)
playbookNamestring | nullYes—
scopeIdstringYes—
scopeKindenumYesOne of: "number_assignment", "number_route", "widget_key".
scopeLabelstring | nullYes—
startsAtstring (date-time) | nullYes—
statusenumYesOne of: "draft", "running", "stopped", "completed".
targetMetricenumYesOne of: "target_attainment", "primary_attainment", "reached_rate", "fallback_rate", "opportunity_rate", "attempt_rate", "conversion_after_attempt", "ai_miss_rate", "content_miss_rate", "operations_miss_rate", "caller_miss_rate", "value_per_eligible", "held_rate", "time_to_attempt".

Errors: 400, 401, 403, 404, 409, 429, 500, with an ErrorBody body.

Example

bash
curl -X POST "$ANSWERSTACK_API_URL/v1/account/experiments/{experimentId}/stop" \
  -H "Authorization: Bearer $ACCESS_TOKEN"