Targeting
Filter who sees a test with AND/OR rules over 18 fields: location, device, traffic source, UTM tags, time, cookies, custom properties.
Targeting
Filter who sees a test with AND/OR rules over 18 fields — location, device, traffic source, UTM tags, time, cookies, and your own custom properties.
URL matching decides which pages a test runs on; targeting decides which visitors see it once they're on those pages. Leave it empty and the test runs for everyone matching the URL — the right default for most experiments. Add rules when you want to compare variants in a specific audience (mobile only, paid social only, enterprise plan customers) or when you're running a personalization test where the rules are the variants.
Targeting rules form a tree. The root is an AND or OR group; each child is either a condition (one field, one operator, one or more values) or another nested group. The SDK and the server both evaluate the tree, with the field deciding where evaluation happens — geo fields resolve from the visitor IP server-side, cookies evaluate in the browser, and most fields work on both sides.
Rule tree at a glance
AND
├── country in ["US", "CA"]
├── device_type equals "mobile"
└── OR
├── utm_source equals "newsletter"
└── custom_property[plan] equals "pro"Read top-down: a visitor matches when they're in the US or Canada, on a mobile device, and either came from the newsletter or has a Pro plan. Mix and nest groups freely.
Field reference
Eighteen built-in fields grouped by what they describe. Server-resolved fields are evaluated from the visitor's IP and mirrored to the client. Server + client fields work on both sides. Client-only fields (cookies) evaluate only in the browser because the server never sees the value.
Location
Geo-IP derived. Resolved server-side and mirrored to the SDK so the browser agrees.countryCountrystringServer-resolvedISO-3166 country code resolved from the visitor IP. Use the country picker for a known list.
country in ["US", "CA", "MX"]
regionRegion / StatestringServer-resolvedSub-country region resolved from the visitor IP. Granularity varies by country.
region equals "California"
Device & browser
Inferred from user-agent. Evaluable everywhere.device_typeDevice typeenumServer + clientdesktop, mobile, or tablet.
device_type equals "mobile"
osOperating systemenumServer + clientwindows, macos, ios, android, linux, other.
os in ["ios", "android"]
browserBrowserenumServer + clientchrome, firefox, safari, edge, opera, other.
browser equals "safari"
screen_widthScreen widthnumberServer + clientReported screen width in CSS pixels. Useful for layout-specific tests.
screen_width greater_than 1280
languageBrowser languagestringServer + clientBCP 47 language tag from the browser, e.g. en-US, pt-BR. Use starts_with for language families.
language starts_with "fr"
Traffic source
Where the visitor came from. Sourced from referrer and URL parameters.traffic_sourceTraffic source bucketenumServer + clientdirect, organic_search, paid_search, social, email, referral. Otter A/B picks the bucket from referrer + utm_medium.
traffic_source in ["paid_search", "social"]
utm_sourceutm_sourcestringServer + clientRaw utm_source from the URL.
utm_source equals "newsletter"
utm_mediumutm_mediumstringServer + clientRaw utm_medium from the URL.
utm_medium contains "cpc"
utm_campaignutm_campaignstringServer + clientRaw utm_campaign from the URL.
utm_campaign starts_with "spring-"
referrerReferrerstringServer + clientDocument referrer (where the visitor came from). Empty for direct navigation.
referrer contains "reddit.com"
Time
Server-side clock + visitor’s local context.time_of_dayTime of daytimeServer + clientLocal visitor time. Supports between for time windows.
time_of_day between [09:00, 17:00]
day_of_weekDay of weekenumServer + clientmonday through sunday.
day_of_week in ["saturday", "sunday"]
Visitor & custom signals
Built-in audience signals plus whatever you push from your code.visitor_typeVisitor typeenumServer + clientnew or returning, derived from the Otter visitor UUID cookie.
visitor_type equals "returning"
url_paramURL parameterkeyedServer + clientTest a specific query string parameter by key.
url_param[plan] equals "pro"
cookieCookiekeyedClient-onlyRead any cookie on the visitor’s browser by name. Client-only because the server never sees the cookie value.
cookie[loggedIn] equals "true"
custom_propertyCustom propertykeyedServer + clientAny property you set via optimo.identify({ ... }). Use for plan, signup date, LTV, internal flags, etc.
custom_property[plan] equals "enterprise"
Operator reference
Operators available depend on the field's data type. The wizard only offers valid combinations.
Equality
Direct comparisons. Work on string, enum, and keyed fields.
equalsnot_equalsinnot_inSubstring (string fields only)
Match against any part of the string. regex evaluates case-insensitively; you anchor it yourself.
containsnot_containsstarts_withregexExistence
Check whether the field is set on the visitor at all.
existsnot_existsNumeric & time
Range comparisons. Available on screen_width (numeric) and time_of_day (time).
greater_thangreater_than_or_equalless_thanless_than_or_equalbetweenTargeting tips
Start broad, narrow later. Most tests should target everyone. Add conditions only when you have a specific audience hypothesis (“this CTA works better for mobile”) or when running personalization.
Prefer signals you control over geo-IP. Cookies, UTM tags, and custom properties from optimo.identify() are deterministic. Geo-IP is mostly right but can mis-classify VPN, corporate, and mobile-carrier traffic.
Be careful with regex. Most targeting bugs come from a regex that looked right but matched the wrong audience. Test the pattern in DevTools (new RegExp(pattern, 'i').test(value)) before saving.
Finalize targeting before launch. Editing targeting mid-test doesn't reassign existing visitors, but it does change who enters going forward, which can shift the variant ratio.
Frequently asked questions
Quick answers to the questions teams ask most about this part of Otter A/B.