- Allocations — the named bundles of parameter overrides the user can be assigned to, each with a bucket range and a share of the policy’s traffic
- Conditions (optional) — context predicates restricting who is eligible
- Eligible bucket range (optional) — restricts the policy to a sub-range of the layer
- Algorithm configuration — only for adaptive policies (Thompson Sampling, contextual bandits, etc.)
- Rollout configuration (optional) — turns the policy into a progressive rollout
Policies are often created for you: when a change moves through its lifecycle, each traffic-carrying phase materializes a policy in the layer with the change’s approved measurement plan attached. You can still create and operate policies directly — a change is governance on top, not a replacement.
Policy states
A policy moves through a small lifecycle:Static vs adaptive
- Static
- Adaptive
Static policies have allocations with fixed bucket ranges. They don’t change unless you explicitly update them or attach a rollout. Use static policies for:
- A/B tests with fixed traffic splits
- Targeted overrides for specific segments
- Holdout groups
Allocations
An allocation maps a bucket range to a set of parameter overrides.- Name — a label like
control,treatment_a,low_price - Bucket range —
[start, end]within the layer’s bucket space - Overrides — parameter keys and the values to use for users in this allocation
Targeting conditions
A policy can declare conditions that restrict eligibility based on context attributes. All conditions must pass (AND).Operators
Which operators are offered for a field depends on the attribute’s registered type — see the type table.
Strict typing
SDKs compare values without coercion.eq is strict equality, in is strict membership, and the relational operators only compare numbers: cart_value gte 100 never matches a context value of "100", and app_version gte "2.0.0" never matches anything. contains, startsWith, endsWith, and regex require both sides to be strings.
The coercion happens at save time instead. When a condition targets a registered attribute, the dashboard and the API convert the condition value to the attribute’s declared type before storing it — so a number attribute stores 100, a boolean stores true, a timestamp stores epoch milliseconds — and validate enum membership, country codes, ranges, and regex syntax. Unregistered fields are stored exactly as entered.
Field lookup
A condition’sfield is looked up in the context in two steps:
- Flat key first. If the context has a property named exactly
field— dots included — that value is used, even when it isnull. - Nested path. Otherwise the field is split on
.and walked into nested objects.
user.plan matches both { "user.plan": "pro" } and { user: { plan: "pro" } }; when both are present the flat key wins. A missing path never errors — it is treated as absent.
Examples
Only users in Germany or Austria:$unit_key is replaced with the layer’s actual unit-key field when the bundle is built; the wizard’s Add test users button inserts it for you:
$env is resolved when each environment’s bundle is built, so the SDK never has to send it:
Adaptive algorithms
Adaptive policies specify an algorithm and a goal:
See Optimization for algorithm details.
Per-entity adaptive policies
A standard A/B test learns one answer for the whole user base. Sometimes that’s the wrong shape. Each product page might have a different best image order. Each merchant might convert better with a different recommendation algorithm. Each user segment might respond to a different email tone. A per-entity adaptive policy runs one bandit per entity:entityKeys— the field(s) incontextthat identify the entity (e.g.productId,userId,merchantId).resolutionMode: "bundle"— entity weights are shipped in the config bundle; the SDK resolves locally (sub-millisecond, but weights are only as fresh as the bundle).resolutionMode: "edge"— the SDK calls Traffical for each decision and gets the latest weights. Higher latency but real-time freshness.
Dynamic allocations
When each entity has a different number of options (e.g. each product has a different image count), usedynamicAllocations:
context.imageCount = 5, the SDK creates allocations ["0", "1", "2", "3", "4"] and selects the one with the highest learned weight. The selected index is reported as the allocation name on the resulting decision.
See the per-entity adaptive pattern for an end-to-end walkthrough.
Contextual bandits (personalized policies)
A contextual bandit personalizes the assignment based on user context features. Different users see different allocations — and the model learns which features predict which allocation performs best. The training pipeline produces coefficients per allocation. Those coefficients ship in the config bundle. At resolution time the SDK computes a score per allocation from the user’s context and selects via softmax — all locally, no network call. Two things you need to know:- Context logging allowlist — only the context fields you explicitly opt in to are logged with decision and exposure events. This protects PII while still giving the trainer signal to learn from. A registered attribute’s
loggingsetting applies on top:alwayskeys are logged for every policy,neverkeys for none. The same allowlist governs server-side resolution, which never writes the raw request context. - Exploration is preserved — the softmax has a temperature (
gamma) and a minimum action probability (actionProbabilityFloor) so the model keeps exploring as it learns.
Eligible bucket ranges
A policy can optionally narrow itself to a sub-range of the layer:Next steps
A/B testing
Run a static policy end-to-end.
Optimization
Adaptive policies, contextual bandits, per-entity bandits.
Rollouts
Turn a policy into a progressive rollout with health checks.
Experimentation patterns
The common patterns and how to model them.