@drip-apex/apex-cli
v0.19.1
Published
Apex CLI for managing experiments, approved roadmap ideas, and draft workspaces.
Readme
Apex CLI
Choosing a workflow
The default loop for agents building or editing a test is apex experiments pull, edit supported files, apex experiments plan, apex experiments apply, then apex experiments qa status.
| Workflow | Use when | What it writes | Do not use when |
| --- | --- | --- | --- |
| apex push | Syncing config-as-code goals, targeting, and flags by slug. | Server configuration declared in .drip.config.ts. | Editing one test's variation files or QA state. |
| apex experiments pull/plan/apply | An agent is building or editing a test. This is the default. | Supported experiment files and server draft state after review of the plan. | You need a live operator code workbench. |
| apex workspace | An operator needs a live-preview draft for one variation. | Workspace draft files and published draft variation code. | You need the default full-experiment agent loop. |
| apex harness | An external ab-harness project needs build and QA integration. | External harness project artifacts and QA sessions. | Apex owns the experiment files directly. |
Purchase-capture verification
The CLI exposes a read-only, publication-anchored purchase verification battery:
apex shops doctor --check purchase_capture
apex tracking verify --event-type goal,conversion,purchase,checkout_completed,order_created --minutes 10080
apex install check --shop-id "$APEX_SHOP_ID"purchase_capture reports OK, DARK, or NO_DATA; a young window or unknown historical cadence
is NO_DATA, never a healthy result. Tracking verification renders compact event-property previews
in human mode and preserves complete properties in --json. Install checks report the served
dataLayerTriggers count and whether an enabled revenue trigger exists.
--shop-id selects the shop for every install-check API request. A definite install-check or doctor failure exits 6; install-check JSON lists error.details.failed_checks.
Authentication and context
Run apex login once to authenticate your account. Interactive login opens
the browser and then lets you select an organization and shop. Inspect the
credential and every accessible context with apex whoami, and switch the
stored default with apex use <org>[/<shop>] or apex use --org <org> --shop
<shop> (IDs and unambiguous name prefixes are accepted). When authentication
comes only from an environment key, apex use returns context_not_persistable
with the DRIP_ORG and DRIP_SHOP values to export instead of writing a credential file.
Every command also accepts --org <id> and --shop <id>. Their precedence is
command flag, then DRIP_ORG / DRIP_SHOP, then the context saved by
apex use. Falling back to the stored apex use context emits one warning on
stderr so scripts can see the implicit selection without corrupting JSON
stdout. Flags and environment variables are explicit choices and stay silent.
JSON envelopes echo the effective org, shop, and credential kind.
Automation should continue injecting a narrow shop key per process with
APEX_API_KEY; it takes precedence over a stored user credential and does not
change the stored login. Use apex login --api-key apx_uk_... for a headless
account login or apex login --api-key apx_... to store a shop key. The old
apex login --user spelling is a deprecated alias for apex login.
Command-line tools for Apex experiments, draft workspaces, GitOps pushes, QA evidence, tracking checks, and agent handoffs.
Install
npm install -g @drip-apex/apex-cliInstall the CLI once, then call apex directly. apex --help and apex schema --json answer without network access. Do not run npx @drip-apex/apex-cli@latest for every command: npx asks the npm registry for the package before the CLI starts, so it prints nothing and hangs when the registry is slow or blocked, which is common in agent sandboxes. Update with npm install -g @drip-apex/apex-cli@latest.
Authenticate
apex login
apex whoamiapex login opens the default browser OAuth flow at Apex /cli/login. For
headless environments, use an existing API key:
apex login --api-key apx_your_key_here
APEX_API_KEY=apx_your_key_here apex experiments list --jsonCommon usage
apex experiments list
apex experiments list --include-archived
apex experiments create --name "Redirect test" --type redirect \
--source-url "https://shop.example/products/source" \
--destination "https://landing.example/products/treatment"
apex experiments pull <id-or-slug> --out ./my-test
apex experiments plan ./my-test --json
apex experiments apply ./my-test --json
apex experiments qa status ./my-test --json
apex experiments qa evidence ./my-test --file ./desktop.png --device desktop
apex experiments qa evidence <id-or-slug> --job <screenshot-job-id>
apex experiments screenshots <id-or-slug> --out ./screenshots
apex workspace checkout <experimentId> --variation <variationId> --out ./workspace
apex workspace dev ./workspace --no-open
apex workspace publish ./workspace
apex push --dry-run
apex schema --jsonUse apex as the canonical command name. drip is a compatibility alias.
apex experiments list returns code-free summaries by default, including
targeting_urls and variation_count, without targeting objects, variations, or goals.
Use --full --json for full details, including code. --full keeps the default table unchanged.
Use --fields id,test_key,name,status --json to select top-level keys (JSON always keeps id),
or --fields status,name to print exactly those table columns in order.
--fields and --full can combine; invalid fields return the allowed keys.
Existing CR-011 filters still narrow the set; these flags only select row content.
The direct API retains its full default view; the CLI requests view=summary.
Summary timestamps include created_at and archived_at; experiments have no updated_at field.
apex experiments list hides archived experiments by default. Pass
--include-archived to include them; archived rows display their archive date
when the API provides archived_at.
JSON output contract
With --json, or whenever stdout is piped, stdout carries exactly one JSON
document and nothing else. Spinners, progress, prompts, warnings, human text and
child-process output go to stderr, so apex ... --json | jq always parses.
{ "ok": true, "version": "0.13.0", "command": "apex flags get", "data": {}, "context": {} }datais the API response body exactly as the API returned it for every command backed by one API request. Lists keep the API's{ "data": [...], "pagination": {...} }shape, so rows are at.data.data[]; single resources are at.datadirectly.apex experiments getadds one CLI-derived key,dashboard_url, beside the experiment fields.- Commands that combine several requests or local files (
experiments create,pull,plan,apply,qa approve,triggers,project-code,harness,workspace,doctor,whoami,push,analytics urls,flags status,goals test) print a documented CLI-composeddataobject. - Failures print
{ "ok": false, "version", "command", "error", "context" }.error.codeis the CLI exit code,error.detailscarries retry hints and server details, anderror.apiis{ "status", "body" }with the API error envelope verbatim whenever an API response caused the failure. deprecatedappears on the envelope only when a deprecated flag was used.
Breaking in 0.13.0. Earlier releases re-nested or renamed API bodies. Update
scripts: .data.experiments[] becomes .data.data[]; experiments get
.data.experiment.id becomes .data.id; .data.flag, .data.goal,
.data.funnel, .data.signal, .data.backlog_item, .data.personalization,
.data.exclusion_group, .data.asset, .data.landing_page, .data.webhook,
.data.targeting and .data.verification wrappers are gone (read the fields at
.data); experiments results reads at .data instead of .data.results,
funnels run at .data instead of .data.run, and brand at .data.data
instead of .data.brandContext; lifecycle and delete commands return the API's
body instead of a CLI summary.
Command index
Use apex schema --json to emit the complete machine-readable command surface,
including every subcommand, argument, option, and alias.
Authentication and account
apex login— authenticate with a shop or user API key.apex logout— remove stored credentials.apex whoami— inspect the current credential and its validity.apex use— select the current organization and shop context.apex upgrade— install the latest Apex CLI;--dry-runprints the command.apex account— manage the current user profile.apex keys— manage shop API keys.apex org— inspect and manage the organization.apex team— manage shop-scoped members and invitations.
Experiments and delivery
apex consent— manage consent gating.apex project-code— inspect and safely change shop project code.apex project-code get— read complete code and its opaque version.apex project-code diff— preview a partial JSON change without writing.apex project-code set— create a backup before a version-checked write.apex triggers— manage data-layer triggers and temporary setup capture.apex triggers get --shop <domain-or-id>— read the identified shop's runtime triggers.apex triggers set— replace the shop's runtime triggers from a JSON file.apex triggers propose— propose candidate triggers from a discovery report.apex triggers apply-recipe— apply a server-owned trigger recipe.apex triggers enable-capture— enable temporary setup capture.apex triggers disable-capture— disable setup capture and clear its expiry.apex domains— manage install domains, and landing-page domains (claim,register,status,unclaim; registering asks for a price confirmation).apex handover— manage cross-domain identity handover.apex install— check storefront installation and generate a client guide.apex install kit— print a ready-to-send installation guide as Markdown or JSON.apex install check— verify the snippet, served config, and recent tracking.apex install steps— show each installation step with when Apex first and last saw it (--livealso checks the page and firewall access).apex funnels— manage analytics funnels.apex webhooks— manage shop webhooks.apex simulate— simulate data-layer triggers without emitting events.apex experiments status [folder-or-id]— read context, file versions, QA, and handover next steps without writing. Defaults to the current pulled folder.apex experiments— create, inspect, operate, and synchronize experiments.apex results— inspect, preview, and import historical CSV or JSON test results. Useapex experiments screenshots <id-or-slug>to force-assign every capture, including control, to its variation server-side for deterministic screenshots. Resume a job that outlives the CLI timeout with--job <id>; polling continues without creating another job, and the stored job's devices and variations win, so--devices/--variationsare ignored (the CLI warns). The command attaches terminal jobs to the QA run by default; use--no-attach-qato opt out. Choose devices with--devices, filter treatments with--variations, and use--jsonfor the signed result payload. Useapex experiments qa evidencewith--job, repeatable--file, or--recordsto write QA evidence directly.apex commerce— look up Shopify products, live prices and costs for commerce drafts.apex push— synchronize.drip.config.tswith Apex.apex preview— open a variation QA preview;--proxyreturns aqa_runreceipt. Itsautomated_evidence_countcounts the normalizedevidenceRecordsacross all evidence kinds, matching the list shown byapex experiments qa status.apex roadmap— consume approved roadmap ideas and handoffs.apex harness— develop AB Test Harness projects with live QA sessions.apex workspace— check out, preview, and publish draft workspace files.apex goals— manage conversion and revenue goals. Create/update accept--page-id <id>(UUID only, no lookup) or--page <id-or-key>(keys needpages:readfor lookup); use one option.apex flags— manage feature flags.apex surveys— list, get, create, update, start, pause, complete, duplicate, and delete surveys. Useapex surveys list --status liveto filter, andapex surveys create @survey.jsonorapex surveys update <id> <body>with inline JSON,@file, or a file path. Both create and update also accept--file survey.jsoninstead of a positional body; providing both is an error. Their--helppages include the example and field defaults below.--jsonreturns the server body unchanged underdata; publication warnings go to stderr. Deleting a live or paused survey returns conflict exit code 5. Complete it before deleting.Example
survey.jsonforapex surveys create --file survey.jsonorapex surveys update <id> --file survey.json:{ "name": "Product feedback", "questions": [{ "type": "single_choice", "prompt": "What would help you decide?", "options": [{"label": "More details"}, {"label": "Reviews"}, {"label": "Delivery info"}] }], "targeting": {"url": [{"type": "simple", "pattern": "/products/", "matchType": "contains"}]}, "sampling_rate": 0.5, "frequency_cap": "once_per_visitor", "card_style": "quiet", "answer_goal": 100 }On create,
nameis required. Server defaults:questions: [](each question'srequired: true),targeting: null(all pages/visitors),sampling_rate: 1,frequency_cap: "once_per_visitor",card_style: "quiet",answer_goal: null,environment_key: "production",placement: "sdk_page",active_from: null, andactive_to: null. Update accepts a partial body and keeps omitted fields unchanged.apex personalizations— manage personalization deliveries.apex backlog— manage experiment backlog items.apex signals— inspect research and opportunity signals.
Analytics, targeting, and tracking
apex tracking— diagnose storefront tracking.apex events— export raw tracking events.apex events export— export all raw events in a fixed ISO 8601 window with automatic pagination.apex environments— inspect delivery environments.apex analytics— read shop analytics.apex pages— manage page targeting resources. Body keys are listed inapex pages create --help(andupdate --help); the CLI refuses aurl_patternor rule pattern whose shape contradictsmatch_type(match_type_mismatch) unless--literalis passed.apex schema --jsoncarries the body schema.apex segments— manage audience segments.apex exclusion-groups— manage mutually exclusive experiment groups.apex targeting— inspect targeting evaluation and coverage.apex audit-log— inspect auditable account activity.apex inbox— use the operator inbox.
Shop configuration and operations
apex external-pixels— manage external pixel snippets.apex assets— manage uploaded assets. Upload returnsdelivery_url; embed that permanent URL in variation code.public_urlexpires after 300 seconds. Delivery is anonymous once the referencing experiment or snippet is published, and resolves through the owning shop's dashboard session or a QA preview before that. Only the absolute https form is recognized as an owned reference; protocol-relative or http forms are not granted at publication.apex landing-pages— build landing pages with your own tools, then apply, preview, deploy and roll them back.apex brand— manage brand context.apex shops— manage shops and active-shop context.apex skills— inspect agent skill guidance.apex schema— emit the machine-readable CLI schema.apex discover— read observed tracking shapes or mine a public GTM container.
Platform-administrator shop commands are intentionally outside the public command inventory:
| Command | Purpose |
| --- | --- |
| apex admin customers move-stores | Platform admin: --to <org-id> --shop <shop-id> (repeatable), --reason <text>, optional --keep-grants. Returns a dry-run plan by default; --confirm performs the audited move. Supports --json. |
| apex admin customers set-category <org-id> | Change an existing account to internal, demo, dogfood or customer with a platform user key. Requires an 8 to 500 character --reason and --confirm. |
| apex admin customers create | Create internal, demo or dogfood accounts with a platform user key. Requires --name, --category, an 8 to 500 character --reason, and --confirm. |
| apex admin shops archive <shopId> | Archive a shop and disable Apex delivery; use --force in non-interactive environments. |
| apex admin shops restore <shopId> | Restore an archived shop while leaving its runtime kill switch unchanged. |
| apex admin shops settings list --shop <uuid-or-domain> | Read every registered per-shop setting, value, override and group with a platform user API key. |
| apex admin shops settings get --shop <uuid-or-domain> --key <key> | Read one setting; unknown keys list the available registry keys. |
| apex admin shops settings set --shop <uuid-or-domain> --key <key> --value <value> | Resolve and pin the shop, then make an audited write. Booleans accept on/off/true/false; numbers must be finite; null clears nullable settings. Sensitive writes require --confirm; optional --reason and --expected-version preserve audit context and concurrency. |
Landing pages built outside Apex
A landing page is built in a checkout folder by any tool (Claude Code, Codex, a static-site build); Apex keeps its versions, goals and deploys.
apex landing-pages create --name "Fall sale" --out ./fall-sale # or: pull <id> --out ./fall-sale
# edit ./fall-sale/index.html and its assets; context/ holds brand, catalog, offers and reviews
apex landing-pages plan ./fall-sale # what would change
apex landing-pages apply ./fall-sale --yes # new version + signed preview link (24 hours)
apex landing-pages deploy ./fall-sale # live on <shop>.apex-page.com, or the host in apex-landing-page.json
apex landing-pages rollback ./fall-sale --version 1
apex landing-pages experiment ./fall-sale --control 1 --variant 2 # draft split; start it in the dashboardThe folder holds the page files (index.html at the root, at most 300 files
and 100 MB; html, css, js, json, svg, images, fonts, txt, xml, webmanifest, mp4 and webm),
apex-landing-page.json and the context/ folder (each pull replaces its JSON
files with the server's current set). Build with relative asset
URLs (Vite base: "./"). apex-landing-page.json sets the name, slug, an
optional host, a domain claimed with apex domains claim <host> (without
one, or --host, a deploy goes to the shop's own instant host
<shop>.apex-page.com), the path (default /<slug>), whether Apex injects the shop snippet
(snippet, on by default), the shop_hosts that receive the visitor's identity
on outbound links (default: the shop's install domains), the goals
(key, name, type click, pageview or custom) and an optional experiment
(control and variant version numbers). Apply refuses to run without --yes
when no terminal can confirm, and refuses with exit 5 when the page changed
since the pull (--force replaces it anyway). Pull refuses to overwrite a
checkout with changes that are not applied, or a folder's own files that are not a
checkout (exit 5; --force discards or replaces them). A deploy makes the page's goals match
apex-landing-page.json, so a goal edited in the dashboard is reset. A deploy or rollback while an
experiment splits the page keeps serving the experiment's two arms and
leaves its goals as they are (new manifest goals are added); the deployed
version serves when the experiment ends, and its goal changes apply at the
next deploy after that.
Which local format am I in?
Inspect the marker under .apex/: experiment-filesystem.json means the
folder belongs to apex experiments pull/plan/apply, landing-page.json means
an apex landing-pages checkout, while session.json
means it is an apex workspace checkout/status/publish workspace. Keep the two
formats in different folders; when the wrong command is run, Apex points to the
matching status command.
Page targeting: URL rules and Pages
Page targeting has two supported authoring paths, and both stay supported.
| Key in targeting | What it is |
| --- | --- |
| url | An array of URL rules: { "type": "simple" \| "regex", "pattern": "/products/*", "include": true, "matchType": "exact" \| "contains" \| "wildcard" \| "regex" }. |
| authorUrl | Read-only, and present on every pull. The subset of url that an author wrote, recorded by the server; an empty array means no author-written rules. An apply ignores it and the server recomputes it. |
| pages | An array of { "id": "<page id>", "include": true } references to saved Pages. |
| pageRules | Read-only. The server's expanded copy of each referenced Page, including its name and its own URL rules. |
| pageMatchMode | "any" (default) or "all" when more than one Page is referenced. |
URL rules work without a saved Page. apex experiments create --source-url
and a hand-written url array in targeting.json are complete targeting on
their own. Nothing rejects them, and no Page is required.
An included Page decides where the test runs. At runtime the URL gate and
the page-rules gate must BOTH match, but the URL gate is an OR over the whole
expanded url list, and a Page referenced with "include": true copies that
Page's own include rules into url. A visitor on the Page therefore passes the
URL gate through the derived copy even when no authored include rule matches,
so while an included Page is referenced your authored INCLUDE rules add no
pages. A Page referenced with "include": false contributes only an exclude
rule, so it can only veto and your authored INCLUDE rules still provide the
positive match. Authored EXCLUDE rules always apply, and detaching an included
Page gives the authored rules the decision back.
apex experiments get prints the result of those two rules as one
Targeting: line, in the same words the dashboard Setup tab uses, for example
2 URL rules (no saved Page) or Cart page and 1 URL rule.
In a pulled targeting.json, url is the expanded rule set and authorUrl is
the authored subset, always present. Edit url and pages; treat authorUrl
and pageRules as output.
Push config format
Run apex push -c <config> to load a JavaScript or TypeScript config. The
module's default export may be an experiment array, an object with an
experiments array, or one experiment object. TypeScript (.ts or .tsx) is
compiled and bundled on the fly with esbuild into a temporary ESM module;
JavaScript (.js, .mjs, or .cjs) is imported directly.
Each experiment accepts these fields:
| Field | Format and mapping |
| --- | --- |
| slug | Required string and unique within the config; identifies the upsert. |
| name | String sent as the experiment name. |
| id | Optional string accepted in the config; it is not sent by push. |
| trafficAllocation | Optional number sent as traffic_allocation; defaults to 1. |
| targeting | Optional value sent as targeting; defaults to null. |
| variations | Array of variation objects described below. |
| goals | Optional array of { id: string }, mapped to goal_ids. |
| description | Optional string; defaults to null. |
| storeUrl / store_url | Optional string aliases sent as store_url; camelCase wins when both are present. |
Each variation accepts:
| Field | Format and mapping |
| --- | --- |
| id | Optional string, used as the fallback slug and name. |
| slug | Optional string; falls back to id. |
| name | Optional string; falls back to id, then "Unnamed". |
| weight | Number sent as the variation weight. |
| isControl / is_control | Optional boolean aliases; defaults to false. |
| mutations | Optional array; defaults to []. |
| css / custom_css | Optional string aliases sent as custom_css; defaults to null. |
| js / custom_js | Optional string aliases sent as custom_js; defaults to null. |
| resetJs / reset_js | Optional string aliases sent as reset_js; defaults to null. |
| antiFlickerSelectors / anti_flicker_selectors | Optional string-array aliases; defaults to []. |
Push validates only that every experiment has a truthy slug, that slugs are
not duplicated within the loaded config, and that the top-level export has one
of the accepted shapes. It does not runtime-validate the other field types,
required TypeScript fields (name, variations, or variation weight), nested
goal/variation shapes, value ranges, or API-level constraints. The command
upserts each experiment by slug with
PUT /api/v1/experiments/by-slug/:slug; an existing slug is updated and a new
slug is created.
Full documentation
Read the CLI reference on the docs site:
- https://docs.drip-apex.com/cli/install-auth
- https://docs.drip-apex.com/cli/commands-output
Maintainers: see the CLI release runbook.
Project code
apex project-code get --shop example.com --json
apex project-code diff --file project-code.json --shop example.com --json
apex project-code set --file project-code.json --shop example.com --backup previous-code.json --jsonThe input is a partial projectCode object, for example { "css": "body { color: red; }" }.
Supported fields are enabled (boolean) and css, helperJs, and js (strings or null).
Each code field permits at most 20,000 characters. Omitted fields remain unchanged.
Null or blank code clears that field. Unknown fields and empty objects are rejected.
GET returns complete code, shop identity, and an opaque version. DIFF returns effective
before/after values and changed fields without writing. SET creates a new backup file
with complete previous code, identity, capture time, and the exact version before PATCH.
It never replaces an existing backup. Backup failures prevent the write. An effective
no-op returns unchanged, without a PATCH or backup file.
The backed-up version becomes expectedVersion unchanged. A conflict returns exit 5,
keeps the backup, and does not retry. Read the latest state and preview it before a new
attempt with a new backup path. Authentication uses exit 2; invalid input uses exit 3.
A successful write returns saved, its diff, backup path, current version, writtenVersion,
and precompile result. A failed precompile is a saved-write warning. All warnings remain
in JSON and are also written to stderr. User keys resolve domains or pin UUID context;
shop keys must match the requested identity before any backup or write.
Classification uses --counting-type standard|setup_test|aa_test|non_program on
apex experiments create (default standard) and apex experiments update.
Classification writes are draft-only. In pulled experiment.json, experimentType
and countingType equal delivery_type and counting_type from
apex experiments get --json. Omitting or setting countingType to null preserves
the stored value. Classification edits invalidate QA approval; paused and running
experiments reject these metadata changes.
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.
Price, shipping, offer, and post-purchase drafts use typed commerce/<kind>.json files. Each file defines the lever once and maps every variation to values or sameAsControl; its serverOwned section is informational and read-only.
Filesystem apply preserves authored local bytes by default. Use
apex experiments apply <folder> --sync to refresh them from stored server bytes.
The receipt's files includes per-file normalizations and submitted_sha256 (null for unsubmitted files), alongside SHA-256 digests for every stored pull path; synced reports
whether a full refresh succeeded. Server-owned QA and launch files are healed.
JSON bodies and checkout JSON files accept a leading UTF-8 BOM, including PowerShell-written files.
Create/get return dashboard_url; apply returns dashboardUrl. These links
switch the signed-in dashboard user's active shop before opening the experiment.
Filesystem apply caches uploaded asset URLs by byte SHA-256 in .apex/experiment-filesystem.json, so unchanged images reuse their URL across applies without rewriting authored files.
--instructions cannot be combined with --out (or its legacy alias --output); file exports always contain the full JSON contract.
Roadmap handoff JSON identifies itself as apex.roadmap_handoff.v1; backlog get/list
items identify themselves as apex.backlog_item.v1, both with schema_version: 1.
The handoff embeds the same closed item projection that backlog responses serve
alongside their existing raw fields. Published schemas and fixtures live in
docs/contracts/experiment-os.
Breaking changes require a new v2 contract; the backlog version is a separate
optimistic concurrency token.
| Key | Source |
| --- | --- |
| approval | Roadmap decision status (proposed without a decision), item stage, and decision's nullable decided_at. Top-level status remains the item lifecycle status. |
| goals | apex_goal_id is the linked experiment goal or null. |
QA status creates a run only when none exists. Reading an existing run never writes
to it: updatedAt is its last stored mutation time, while computedAt is the ISO
timestamp when the current checklist and summary were computed.
Decision latency
apex tracking latency <experiment-id-or-key> [--days <1..30>] [--qa-session <id>] [--json]
compares forced-preview and natural decision latency before launch. The default
window is seven days. The table shows sample counts, decision p50/p95/p99,
config-fetch p50/p95 (milliseconds), and inline/cache/network/none counts.
JSON output includes the API body in the standard envelope and assignment-ready
percentiles. Fewer than five measured samples have null percentiles. Natural
loads are shop-wide non-QA loads without forced IDs; a QA-session bucket can
overlap forced loads. Analytics failures return errors. The API position is
runtime_inline_no_change.
The runtime remains unchanged. Natural runtime decides within tens of milliseconds
of SDK start when config ships inline with the snippet (measured p50 12 ms /
p95 54 ms on a large storefront). QA previews, screenshot-mode previews and
cookie-forced sessions take the QA config lane and fetch fresh config over the
network (measured p50 1.3 s / p95 7 s); they are not representative of launch
behaviour. URL-forced sessions without a qa parameter keep the inline or cached
config, so these measurements do not describe them. No config preload or
anti-flicker hold is planned.
Connection troubleshooting
Connection errors retain the underlying failure. JSON output exposes
error.details.reason and, when available, error.details.code; the numeric
error.code remains the CLI exit code.
| Reason | Meaning and usual fix |
| --- | --- |
| dns | The host could not be resolved. Check APEX_BASE_URL (or DRIP_BASE_URL), DNS, and whether your sandbox permits network access. |
| refused | The host rejected the connection. Check the base URL, port, and whether the server is running. |
| reset | The connection was interrupted. Check network stability and proxy settings before retrying. |
| tls | Certificate or TLS validation failed. Check expired certificates and your corporate TLS proxy's trusted CA configuration. |
| timeout | The request was aborted or exceeded its deadline. Check server availability and network access. |
| unknown | Another network failure occurred. Use the included cause message to diagnose network or sandbox restrictions. |
Read-only order journey
apex analytics order-journey --platform shopware --order-id <id> --environment production --from <iso> --to <iso> --jsonRequires analytics:read through a secret or authorized user key. Supply exactly one of --order-id or --order-number, an explicit environment and an ISO interval of at most 31 days. --include-qa includes QA eligibility; bots remain excluded. The sanitized report includes all represented assignments and discloses incomplete coverage. Source failures and historical consent/fingerprint gaps remain unknown. Exit 0 means the report was fetched, not that the journey passed. eligible_for_attributed_goal_read uses only attributed_goal_counts; finalStatisticalContribution is always not_evaluated. Nothing is repaired or saved locally.
Order journey diagnostics report current, stale, or unknown freshness for each retained receipt and assignment. Present attributed-goal reads identify their evaluated snapshot; they do not establish historical contribution or repair missing producer evidence.
Error recovery and update notices
JSON errors keep the numeric error.code exit code and add error.reason,
error.next_steps (an array of { command, why }), and optional error.request_id.
The request ID comes from x-request-id, falling back to x-vercel-id (the Apex app) and then cf-ray; include it
when reporting an API failure. API-provided reason codes are preserved verbatim.
Otherwise reasons are auth_required, auth_forbidden, shop_context_required, shop_context_mismatch, install_check_failed,
validation_failed, confirmation_required, conflict, not_found,
rate_limited, network_timeout, network_connect_timeout, network_dns,
network_refused, network_reset, network_tls, network_unknown,
local_file_error, or internal_error.
Inspect next_steps for login, key permissions, shop selection, network settings,
folder validation, or rate-limit recovery. Steps are suggestions, never automatic
approval to retry a write. Confirmation errors retain error.details.retry.
Human output prints recovery steps as Next: lines on stderr and request IDs
on the error line. Trigger commands now follow the same exit codes as every other
command: authentication failures exit 2 (previously 4), invalid local input exits
3 (previously 2), and other API failures follow their HTTP status (validation
3, conflict 5, anything else 1). The TRIGGERS_AUTH, TRIGGERS_CONFIG_ABSENT and TRIGGERS_NO_DATA
verdict prefixes are unchanged, so scripts can key on them instead of exit codes.
Every JSON envelope may carry update and warnings. The update object includes
current, latest, optional minimum, required, breaking, command, and
notes_url; warnings use { code, message }. The exact install command is
npm install -g @drip-apex/apex-cli@latest, and apex upgrade runs it (--dry-run
only prints it). Set APEX_NPM_BIN to choose the npm binary.
The npm latest version is cached for 24 hours (failed lookups for one hour). Server
latest/minimum policy is cached for seven days, so offline commands still enforce a
known floor. APEX_NO_UPDATE_CHECK=1 disables the npm lookup and optional update
notice, but never hides a required server-floor upgrade.
Windows: UTF-8 arguments
A Windows console or launcher can decode UTF-8 as Windows-1252, turning A – B · C
into A – B · C. On Windows, Apex automatically repairs reversible argument
values at startup and writes a note to stderr. Set APEX_ARGV_ENCODING=off to
keep arguments unchanged, or APEX_ARGV_ENCODING=repair to enable repair on
another platform.
Use UTF-8 in PowerShell:
[Console]::InputEncoding = [Console]::OutputEncoding = [Text.Encoding]::UTF8For Git Bash, set LANG=C.UTF-8 and run chcp 65001.
A replacement character (�, U+FFFD) means the shell already lost a character.
Apex sends that value as given and warns on stderr. It also warns when stored
experiment names, descriptions, variation names, or goal names contain U+FFFD.
These notes leave JSON stdout unchanged.
Usage telemetry
Authenticated invocations send a process invocation ID, full command path, explicitly set
option names (never values), JSON/TTY modes, credential kind, organization/shop IDs,
CLI/Node versions, platform, exit code, error class and code, API response count, the first
failed response's status/code/request ID and normalized route, and total duration. Up to the
first 25 API calls include their method, normalized route, status, duration, and bounded
Server-Timing header. Requests whose path contains an experiment or landing-page UUID also
send that UUID and its entity type; slugs are not captured.
Help commands are named (help) <full command path>. Parse failures are named (parse) and
send the known command path plus only a mistyped subcommand as attempted. Potential
credentials, URLs, paths, long keys, and other sensitive-looking attempted tokens are
redacted; positional argument values are never sent.
Agent hosts can be reported as a fixed label such as claude-code, codex, or terminal,
never as an environment value. Set APEX_AGENT_HOST to one of claude-code, codex,
cursor, grok, gemini, copilot, or other to override automatic detection.
APEX_SESSION_ID may supply a 1–128 character identifier containing letters, digits, dots,
underscores, colons, or hyphens; choose a non-secret value. When that variable is absent or
invalid, supported agent session IDs are SHA-256 hashed locally and only the h_-prefixed
first 32 hexadecimal characters are sent.
No argument or option values, file contents or paths, prompts, selectors, URLs, error text,
environment values, raw agent session IDs, or secrets are sent. Login/logout, tests, the mock
transport, and runs with no resolvable credentials (neither stored nor environment) skip
telemetry. Environment-credential runs (APEX_API_KEY or DRIP_API_KEY) are recorded with
credential kind env unless APEX_TELEMETRY=0. Set APEX_TELEMETRY=0 to disable the beacon.
The best-effort request has an 800 ms timeout and never changes command output or exit status.
MP4 (video/mp4) and WebM (video/webm) videos are hosted directly by Apex.
Each version holds at most 300 files and 100 MB; each video is limited to 50 MB,
and other files to 3 MB. apply automatically obtains a 15-minute upload grant
for videos above 3 MB and sends resumable 8 MB parts straight to the Worker,
retrying failed parts with progress output. The Worker verifies the declared
size and SHA-256 before apply records the file. Pull and signed previews also
fetch large videos directly from the Worker. Video supports byte ranges,
including Safari's bytes=0-1 probe; rollback preserves the older bytes.
Engineering account creation uses apex login --user credentials with admin:write
and a live platform role with onboarding write permission. Real customer accounts
are created in Admin, Links.
apex admin customers create --name "Demo workspace" --category demo --reason "Workshop demo account" --confirm --json
apex admin customers create --name "Dogfood store" --category dogfood --domain dogfood.example.com --platform woo --owner [email protected] --reason "Engineering dogfood account" --confirm
apex admin customers set-category 22222222-2222-4222-8222-222222222222 --category internal --reason "Engineering dev store" --confirm --json--type defaults to company (company|agency); --plan defaults to
self_managed (self_managed|apex_managed|drip_managed). Managed plans require
--consultant <platform_access UUID> and --consultant-title <text>.
An optional first store uses --domain <host> --platform <shopify|shopware|woo|other>
or --claim-shop <UUID>, exclusively. woo maps to woocommerce and other to
custom. --owner <email> sends an owner invitation and requires a store;
accounts without a store have no invitations. Output includes the organization ID,
store ID (or null), invitation results and partial delivery status.
Import past results
Upload CSV or JSON exports with apex results import --file results.csv --dry-run. Apex suggests column and outcome-value mappings from the file; use repeated --map 'Column=field' and --outcome-value 'Source value=won|lost|inconclusive' overrides, then pass --yes for non-interactive confirmation. Files are inspected and previewed before any rows are written. --source <name> applies to the confirmed rows; the preview shows the uploaded file name.
Tracking health
apex tracking health [--json] reads one tracking-health report for the current shop: every check with pass, warn, fail or unknown, a plain summary and the deciding numbers, plus each running test's first-hour verdict and traffic split overall and per device (--json carries visitors per arm per day). Exit code 0 is pass, 1 is warn or fail, 3 is unknown.
