Developer

Experiment lifecycle commands

Create, list, inspect, read results, pause, complete, and delete Apex experiments from the CLI.

Human results output names each goal’s counting basis. See Metric definitions for the exact rules.

The experiments group covers experiment CRUD and lifecycle actions. Use IDs or slugs for reads and lifecycle commands. The alias apex exp is available for the group, and apex experiments list also has the ls alias.

List and inspect

bash
apex experiments list --status draft --limit 20
apex experiments list --url https://shop.example/products/example --json
apex experiments get homepage-hero-test
apex experiments results homepage-hero-test --currency EUR
CommandArgumentsFlagsNotes
apex experiments listNone--status <status>, --url <absolute-url>, --limit <n>, --include-archived, --fields <list>, --fullSummary by default; status and effective URL targeting are filtered server-side. Experiments without URL rules match every URL. Select columns with --fields or request full details with --full.
apex experiments get<id-or-slug>None beyond global flagsA plain slug and slug:<slug> both resolve by slug.
apex experiments results<id-or-slug>--currency <code>, --wait <seconds>Returns experiment totals, arms, decision, metrics, series, computed time/source, and warnings. Cold results builds retry for up to 120 seconds by default; use --wait 0 to disable retries.

Use JSON mode for scripts:

bash
apex experiments results homepage-hero-test --currency EUR --json

Lifecycle readback

Create and get JSON always include these keys; they are never omitted. All three are null on a fresh draft. Set values are ISO-8601 UTC timestamps in the exact YYYY-MM-DDTHH:mm:ss.sssZ form.

KeySet byCleared by
started_atDashboard launchRevert to draft
scheduled_start_atLaunch schedulingSchedule cancellation or QA reopen
qa_approved_atQA approvalQA reopen

Integrations reading a schedule field must read scheduled_start_at.

Create a draft

bash
apex experiments create \
  --name "Homepage hero trust copy" \
  --slug homepage-hero-trust-copy \
  --description "Tests clearer proof copy in the homepage hero." \
  --page-url "https://shop.example/products/hero-bundle" \
  --audience segment_vip \
  --goal "new:type=click,selector=.hero-cta,name=Hero CTA click,page=<page-id>" \
  --traffic 25 \
  --preview
FlagRequiredBehavior
--name <name>Headless: yesExperiment display name. In a terminal the wizard asks for it when omitted.
--slug <slug>NoURL-safe slug for GitOps and slug-based lookup.
--description <desc>NoDraft description.
--counting-type <type>Nostandard (default), setup_test, aa_test, or non_program.
--type <type>Nomutation (default) or flag-gated redirect.
--source-url <pattern>Redirect: yesPositive source URL targeting rule. Repeat for multiple exact URLs, paths, or wildcard patterns. The first absolute HTTP(S) value also becomes the preview/store URL.
--page-url <pattern>NoPage URL targeting rule. Repeat for multiple patterns. This is additive with the existing --source-url spelling.
--audience <segment-id|new:@file.json>NoInclude an existing segment, or create one first from the same JSON body accepted by apex segments create. Repeat for multiple audiences.
--goal <goal-id|new:fields>NoAttach an existing goal, or create one from comma-separated type, name, and goal fields such as selector. Field keys are the v1 goal fields; page is accepted for page_id and hyphenated spellings such as text-match and url-pattern are accepted too. Click goals are page-scoped in v1, so an inline type=click goal needs page=<page-id> (list them with apex pages list); in a terminal the wizard asks for the page when it is missing. Wrap a value that contains commas in double quotes, for example selector=".buy,.checkout". Custom goals cannot use reserved event names such as pageview or purchase (add_to_cart and checkout_started are allowed). Every inline goal and audience file is checked before anything is created. Repeat for multiple goals; the first is primary.
--destination <url>Redirect: yesHTTP(S) destination for one treatment variation. Repeat to create multiple equally weighted treatments. Invalid or missing redirect destinations fail validation.
--traffic <pct>NoPercentage input converted to API traffic allocation. Defaults to 100.
--previewNoPrint a QA URL pinned to the first non-control variation. Needs an absolute HTTP(S) --page-url/--source-url to build the link, checked before anything is created, so a run that cannot be previewed fails validation instead of leaving an experiment behind. JSON output includes it as preview_url.
--yesNoDisable wizard prompts. Missing required values then return validation exit code 3.

In an interactive terminal without --yes, omitted values are collected by a wizard that lists available audiences and goals and permits none. Headless callers should provide --name and use flags for the desired targeting and goals. New audiences are created first, then new goals, followed by a single experiment request. If that final request fails, the error lists resource IDs that were already created so callers can clean them up deliberately.

JSON output's data payload has the shape { experiment, segments, goals, preview_url, preview_note }; segments and goals contain resources created by this invocation. Inline goal bodies are parsed and completed before the first write, so a malformed or page-less new: goal fails validation without creating anything. When the created experiment has no treatment variation yet, the command still succeeds and reports why the link is missing in preview_note.

Create a redirect experiment with a control on the source page and one treatment on the destination:

bash
apex experiments create \
  --name "Product page split URL" \
  --type redirect \
  --source-url "https://shop.example/products/source" \
  --destination "https://landing.example/products/treatment"

Redirect creation preserves the public v1 gates: the shop must have redirect tests enabled, at least one positive source rule and treatment destination are required, and destinations must be HTTP(S). A destination must not match the source targeting unless it is the same page with added or changed query parameters, such as ?view=alternate. API gate failures use the standard JSON error envelope and exit codes.

The filesystem and public v1 contracts permit a redirect treatment to retain mutations, CSS, or JavaScript. Those artifacts are source-page-only; Apex keeps the assignment active on the destination for attribution but does not apply the source-authored artifacts there. Filesystem plans warn when a treatment mixes these artifacts with a destination.

Update

bash
apex experiments update homepage-hero-trust-copy --counting-type aa_test --json
apex experiments update homepage-hero-trust-copy --counting-type non_program --results-weekdays mon,tue --results-timezone Europe/Berlin --json

--counting-type accepts standard, setup_test, aa_test, or non_program. It works alone or with the results weekday window. When setting a window, --results-weekdays and --results-timezone must travel together; use --clear-results-weekdays to clear it. Omitting classification leaves it unchanged. Text output reports the classification stored by the server. API edits are draft-only; the CLI passes through the server error for other statuses.

Lifecycle verbs

bash
apex experiments pause homepage-hero-trust-copy
apex experiments complete homepage-hero-trust-copy
CommandArgumentsBehavior
apex experiments pause<id-or-slug>Requests the API to pause a running experiment.
apex experiments complete<id-or-slug>Requests the API to complete or stop the experiment.

Starting traffic is dashboard-only so the approval gates remain visible. Lifecycle validation for pause and complete is owned by Apex; if the API rejects a state transition, the CLI reports the API error and exits non-zero.

Delete

bash
apex experiments delete homepage-hero-trust-copy
apex experiments delete homepage-hero-trust-copy --force --json

apex experiments delete prompts before deleting in an interactive terminal. In non-TTY mode, deletion returns confirmation-required exit code 4 unless you pass --force.

Launch in the dashboard

The CLI has no experiments start command. Launch is dashboard-only so Apex can keep approval and readiness gates visible; CLI lifecycle commands cannot bypass QA or business approval.

Experiment references accept an ID, slug, or test key (for example BIO-085). Test keys resolve within the selected shop; use --shop <shop-id> to change it. Bare lowercase references remain slugs.

Create and get JSON include dashboard_url (null when no shop is known), and text output prints a Dashboard line. The link opens the experiment in the right shop, requires a signed-in dashboard user, and switches that user's active shop. The response's shop_id takes precedence over the CLI's pinned shop context.

Troubleshooting

Results verification: When publishing or interpreting material results changes, require revision-specific live-model comparison evidence and an author explanation.

  • Experiment not found: check whether you passed an ID, slug, or slug:<slug>.
  • Lifecycle action rejected: inspect the API error in JSON mode for readiness, status, or permission details.
  • Delete fails in automation: rerun with --force only after confirming the target experiment.

List summaries and selected fields

apex experiments list --json returns code-free summaries with targeting_urls and variation_count. It omits targeting objects, variations, and goals. Use --full --json when you need the full configuration, including code.

bash
apex experiments list --fields id,test_key,name,status --json
apex experiments list --fields status,name
apex experiments list --full --fields id,name,targeting --json

--fields selects top-level keys. Requesting a field outside the summary view automatically switches the server to full details, so --full is not required. Table columns follow the requested order; JSON always retains id. Unknown keys return an error listing allowed keys. --full leaves the default table columns unchanged. The existing CR-011 filters remain the way to narrow the experiment set; view and field selection change row content, not which experiments match. Direct API consumers retain the full view by default; the CLI sends view=summary. Summary timestamps include created_at and archived_at; experiments have no updated_at field.

Explaining a number

Run apex experiments results GET-023 --explain --samples to compare raw events with reported results. Use --goal <id-or-event-key> for a specific attached goal, --currency EUR for money, or --order <order-key> for an order's label trail across arms. --json preserves the API body, including its data envelope.

Each row gets one label, in this order: bot, qa_traffic, outside_window, duplicate, before_exposure, exposed_before_window_only, filtered_by_goal, or counted. These mean bot traffic, QA traffic, events outside the result window, repeated event/order identity, conversions before the first in-window exposure, visitors exposed only before the window, unmatched or unevaluable goal conditions, and included events respectively. Non-purchase events without an eligible exposure, and placeholder visitor IDs on exposures and non-purchase goals, are also filtered_by_goal; purchases keep placeholder IDs, as the results pipeline does. An order belongs to the variation on its earliest row; if that variation is no longer part of the test, the order appears under other_variations. An order without any tracked exposure carries arm_only: true when counted.

The table shows counted, reported, and residual (counted minus reported) for each arm. This is a diagnostic classifier, not the canonical results pipeline. A nonzero residual exposes a disagreement between the diagnostic rules and the results pipeline; it is never forced to zero. Unavailable reported values or currency conversion produce an unavailable residual. The scan includes 30 days before the result window; older sightings cannot be diagnosed. Exposure rows are aggregated without a raw-row cap, so visitor counts remain exact; visitor samples are not provided. Variations missing from the results model appear in other_variations and are excluded from the arm diagnostics.

Conversion scans above 100,000 rows return diagnostic_row_limit with the observed row count and a hint to narrow by --order. An order lookup restricts candidates to that order and exposures to its visitors. Because it covers one order, it shows no reported value or residual; run without --order to compare with the reported numbers. Timeouts and oversized candidate scans return errors, never partial results. See metric definitions.

Profit per visitor for commerce tests and checkout-owned tests

apex experiments results includes Profit per visitor (CM1) for commerce tests and checkout-owned tests with attributed orders. It reports exposed visitors, attributed orders, CM1 per visitor, the range versus control, the verdict, cost coverage, and failed trust checks. Unsupported money is -, not zero. A pending or unavailable analytics read prints its degraded reason instead of an empty results table.

Checkout-owned tests measure orders from visitors who saw the checkout message and receive the supplementary CM1 section when they have attributed orders. Other non-commerce tests return commerce_results: null. An unavailable supplementary section on a checkout-owned test does not mark the analytics as degraded.