Intempt Docs
GuidesExperiments

Experience types

Four types, two stored fields. What each one is, which SDKs can serve it today, and what its numbers let you conclude.

An Experience answers one of two questions and is applied one of two ways. Those two choices are the whole model, and they are the only two things stored.

Applied by the visual editorApplied by your code
Does this work? — split traffic, measurePage experimentServer experiment
Who has this yet? — serve to a sharePersonalizationFeature flag

Everything else on this page is a setting inside one of those four.

Which surfaces each type reaches

The visual editor works on web pages only, because only a web page has a DOM to point at. Your code covers a backend, a mobile app or a web page identically. That is the model; the callout below says how much of it an SDK can actually serve today.

TypeWeb pageBackendMobile app
Page experiment
Personalization
Server experiment
Feature flag

Reading an Experience from your own code is not shipped yet. A Server experiment or a Feature flag needs an SDK that asks the choose API for a key, and one SDK does: the Java SDK, which has no published artifact — you build it from the repository.

The browser SDK calls optimization/choose-web and applies visual-editor changes, so Page experiments and Personalizations work on a web page today. It has no read-by-key call.

Node.js, Android, iOS, React Native, Python and PHP cannot serve an Experience. They track events and manage consent; the optimization call is not implemented, and Node.js throws from its 1.x experience helpers rather than returning an empty result that would read as "no variant". The work sits in open SDK branches, so check your SDK's page before planning against these two types.


Page experiment

A/B test a headline, a layout, a price, on your own web page. No deploy.

How it works. On someone's first evaluation Intempt rolls a number from 0 to 99 and walks your variants in id order, adding each one's traffic share until the roll lands inside a band. The winning variant is written against that person, so they keep it for the life of the test.

Two variants or twenty — there is no separate "multivariate" type, the platform counts them. Three or more variants requires the Multivariate entitlement on the Organization plan.

What the numbers mean. A real controlled experiment. Assignment is random, both arms are exposed and recorded, and nothing but the roll separates the groups. Significance, confidence intervals, CUPED and sequential testing all apply as written. With three or more variants a Benjamini-Hochberg correction is applied across the arms — read the corrected column.


Server experiment

The same test, on a value your own code branches on. A ranking, a model, a checkout path.

Your code asks Intempt what to show and applies it:

String layout = client.stringVariation("checkout_layout", context, "classic");

Each variant holds a JSON payload picked from your content library rather than on-page edits. The payload reaches your SDK as authored.

How it works. Identical allocation to a Page experiment. The only difference is who applies the change.


Personalization

Decide who should see what on your page, and always show them that. You are not asking which version is better.

How it works. The rules are evaluated in the database before any application code runs. The variant list arrives already filtered to the ones this person qualifies for — by audience, device, URL and time window — and the engine takes the lowest priority number among them. That is first-matching-rule-wins, which is why you can stack many audiences on one Experience.

A Reach percentage is rolled first. Whoever falls outside it sees nothing and is recorded as control.

What the numbers mean. Not a controlled experiment by default. The people seeing the loyalty banner differ from everyone else in the way that got them targeted, so a gap between the groups is not attributable to the banner.

Set Reach below 100 and it becomes one: a random slice of the same qualifying audience is withheld, which is a proper control. Without that, read the numbers as description, not as cause.


Feature flag

A Personalization your own code reads by key. Ship to 10% of people, then to everyone. Or to one audience before anybody else.

boolean newCheckout = client.boolVariation("new_checkout", context, false);

allFlags(context) returns every flag assigned to that person in one call.

How it works. The same serve engine as a Personalization, and the same per-variant audience targeting — your call carries an identity, Intempt resolves that person's segments, and the same database filter runs. What comes back is your variant's JSON payload.

This is how you personalize a mobile app or a backend. There is no separate mobile mechanism.

Reach and audience together

Both are fields on the same Experience, and neither excludes the other:

AudienceReachResult
All users5%a gradual rollout
Beta users100%released to one group first
Beta users25%a quarter of beta users

Read this before planning a ramp. A serve-mode Experience stores whether someone is in, not which variant they got.

  • People already in stay in when you move Reach.
  • People already held back stay held back. Their exclusion was written on first evaluation and is not re-rolled. Moving 5% to 50% reaches people the Experience has not evaluated before.
  • Lowering Reach does not take it away from anyone who has it.

If you need a clean reset, create a new Experience — storage is keyed by Experience id, so a new one starts empty.

What the numbers mean. A rollout is a release, not a test. Whoever arrived early differs from whoever arrives later in ways you did not control. To learn whether the new checkout is better, run an experiment. To ship it safely, use this, and watch your error rate.


What the two engines do differently

Split — the two experimentsServe — Personalization and Feature flag
How someone is chosenrandom roll across variant traffic sharesReach roll, then lowest-priority matching rule
Stored per personthe variant idonly in or out
On a later callthe same variant is replayedeligibility is re-checked against today's rules
Per-variant audiencedo not, see belowthe point of it

Both publish an exposure event on both arms, so measurement never depends on which you picked.

Do not put an audience on a single variant inside an experiment.

A targeted arm is filtered out for anyone who does not match, the remaining arms absorb that traffic, and the arms stop being comparable populations. You still get a result, it still reports significance, and it is not measuring what you think. On an experiment, an audience belongs on the Experience, where it decides who is eligible for the test at all.

Targeting, by surface

OptionVisual editorYour code
Audience
Reach
Schedule
Devicesee below
URL conditions
Display frequencybrowser and mobile yes; a backend has no session

Device. A browser reports its device from the user agent. The Java SDK sends all, which matches every device rule — so a variant targeted at a specific device will not match it. No other SDK reaches this API yet, so there is nothing else to report.

URL conditions describe a page location, which a call from your code does not have.

Display frequency — every time, once, or once per session — is enforced wherever it can be.

Every time and once work everywhere. Once per session needs a session, and a backend does not have one. That is a deliberate design decision, not a gap: a server handles requests, not visits, so there is nothing for a session to be scoped to. On a backend SDK, once per session behaves as every time.

A browser and a mobile app both have real sessions, so the rule applies there as written.

Because "applied by your code" covers a backend and a mobile app with the same stored value, the console cannot tell which one will call. The control is offered on both, and it is the SDK that determines whether it can mean anything.

Reporting

Results are reported per Experience and per variant. They are not currently split by surface — an Experience served to both a mobile app and a backend reports one set of numbers, not two.

Without permission to edit

Write controls stay visible and disabled, each carrying its reason, rather than disappearing. Someone who cannot find a control assumes the product lacks it and asks for a feature that already exists.

The console is half of it: the API refuses the same writes independently. A disabled button is a courtesy; the refusal is the authorization.

Next

On this page