Skip to main content
Every experiment measures at least one thing. That thing is a goal. It might be a click on a button, a page visit, a form submission, or a custom event you fire from your own code. Each experiment picks its goals from your site’s goal library.
Goals library

Site → Goals. The library of every goal defined on this site.

What a goal is

Three pieces:
  • A name, human-readable, shows up on the results page.
  • A short key, machine-readable, used in abtestly.trackGoal('key').
  • A definition, what actually causes it to fire. Seven kinds; see Goal types.

Primary vs guardrail

An experiment has:
  • Exactly one primary goal, the one that decides “did the test win?”.
  • Any number of guardrails, goals that catch collateral damage. A primary win with a breached guardrail is called out on the results page so you can decide whether to ship it.
See Primary vs guardrail for how to pick. Two goals attach automatically as guardrails on every new experiment: Increase Engagement and Engaged session. See System defaults.

The library

Every goal on your site lives in the library, inline goals do not exist as a separate concept. If you create a goal directly on an experiment, it goes into the library and gets marked as attached to that experiment. Other experiments can then attach the same goal. Goals in the library have a status:
  • In use, attached to at least one experiment.
  • Idle, defined but not attached.
  • Locked, attached to a live experiment or shared across more than one experiment. Locked goals cannot be edited directly. Click Clone to edit to make an editable copy.
  • System, auto-seeded, cannot be edited or deleted.

How ABTestly counts a fire

For each variant exposure, ABTestly tracks the first fire of each goal per visitor per experiment. Refires do not count again, a visitor who clicks the CTA three times counts once. This is deliberate; otherwise a visitor’s engagement inflates their arm’s conversion rate in an untrustworthy way. The exception is revenue goals. Revenue is summed across all fires, because the second purchase is a real second purchase. Revenue deduplication uses orderId so a retried beacon does not double-count.

Where a goal can fire

A goal counts when it fires on a page the experiment covers, and it is credited to the variant the visitor was assigned. On a site with SPA support, the fire is checked against the current route at the moment it happens, so a soft navigation away from a covered page stops goals firing rather than counting them at the old assignment. The one exception is the system Engaged session goal. It is credited across the whole session regardless of route, so a visitor exposed on /pricing who then loads /checkout qualifies the /pricing experiment even though the experiment does not run on /checkout. Everything else, including a page visit goal pointed at a confirmation page, needs that page to be inside the experiment’s Location. See Goal health for how that shows up when it is missing, and why widening the Location is not always the right fix.

The health signal

The results page watches your primary goal and shows a banner when it looks broken:
  • Not firing, zero conversions across at least 200 visitors.
  • Converting on one variation only, when at least two variations have real traffic and only one of them converts.
Both are advisory. Neither hides the results or blocks a verdict. See Goal health for the thresholds and how to fix each.

Common pattern: primary + system guardrails

For most tests, the shape is:
  • Primary: the specific thing you care about, a CTA click, a purchase, a signup.
  • Guardrails: the two system goals (attached automatically).
That is enough to catch the “wins the CTA click but crashes engagement” class of accident without any per-test setup.
Last modified on August 28, 2026