Targeting
Targeting lets you control which users see a feature based on their attributes. Instead of turning a flag on or off for everyone, you define rules that match specific users or segments.
How targeting works
- You set targeting rules on a flag-environment (e.g.
checkout-redesignindevelopment) - The SDK calls the evaluate endpoint with the user’s context
- If the flag is disabled, rules are skipped entirely and the flag’s
offValueis returned - If the flag is enabled, rules are checked in priority order — the first match wins
- If no rule matches (or no rules are defined), the flag’s
defaultValueis returned — this is the fallthrough value
const result = await flagify.evaluate('checkout-redesign', {
id: 'user-456',
plan: 'pro',
country: 'US',
});
// result: { key: "checkout-redesign", value: true, reason: "targeting_rule" }
Targeting rules
Each flag-environment can have multiple targeting rules, ordered by priority. A rule can match via:
- Segment — reference a reusable segment (e.g. “Pro Users”)
- Inline conditions — conditions attached directly to the rule
- Both — segment AND conditions must all match
- Neither — catch-all rule that always matches
Each rule can optionally include:
valueOverride— the value to return when this rule matchesrolloutPercentage— only apply to X% of matching users
Setting rules via the API
curl -X PUT /v1/flag-environments/{feid}/targeting-rules \
-d '{
"rules": [
{
"segmentId": "seg_pro_users",
"valueOverride": "pro-checkout",
"enabled": true
},
{
"conditions": [
{ "attribute": "country", "operator": "equals", "value": "US" }
],
"valueOverride": "us-checkout",
"enabled": true
}
]
}'
Rules use a replace-all pattern — each PUT replaces all existing rules.
Supported operators
| Operator | Description | Example |
|---|---|---|
equals | Exact match | plan equals "pro" |
not_equals | Inverse match | plan not_equals "free" |
contains | Substring match | email contains "@acme.com" |
not_contains | Inverse substring | email not_contains "test" |
starts_with | Prefix match | email starts_with "admin" |
ends_with | Suffix match | email ends_with ".edu" |
in | Value in list | country in ["US", "CA", "UK"] |
not_in | Value not in list | country not_in ["CN", "RU"] |
gt | Greater than (numeric) | age gt 18 |
lt | Less than (numeric) | age lt 65 |
Percentage rollouts
Roll out a feature to a percentage of users:
{
"rolloutPercentage": 25,
"valueOverride": "new-experience",
"enabled": true
}
Flagify uses consistent hashing (murmur3(flagKey + ":" + userId) % 100) so the same user always gets the same result for a given flag. Gradually increase the percentage to ramp up exposure.
User segments
Segments are reusable groups of users, scoped to a project. Each segment has rules with a match type:
- ALL — all rules must match (AND logic)
- ANY — at least one rule must match (OR logic)
curl -X POST /v1/projects/{pid}/segments \
-d '{
"name": "Pro Users",
"matchType": "ALL",
"rules": [
{ "attribute": "plan", "operator": "equals", "value": "pro" }
]
}'
Reference segments in targeting rules by their ID. Updating a segment automatically affects all flags that reference it.
Evaluate endpoint
The SDK’s evaluate() method calls the API with user context:
import { Flagify } from '@flagify/node';
const flagify = new Flagify({
projectKey: 'my-project',
publicKey: 'pk_dev_abc123_xxx',
});
const result = await flagify.evaluate('checkout-flow', {
id: 'user-123',
plan: 'pro',
country: 'US',
});
if (result.reason === 'targeting_rule') {
// User matched a targeting rule
renderCheckout(result.value);
} else {
// Default value
renderCheckout(result.value);
}
Response
{
"key": "checkout-flow",
"value": "pro-checkout",
"reason": "targeting_rule"
}
| Reason | Description |
|---|---|
targeting_rule | A targeting rule with conditions/segment matched |
rollout | A rollout percentage rule matched |
no_match | Targeting rules exist but none matched — returned flag’s defaultValue |
default | No targeting rules defined — returned flag’s defaultValue |
disabled | Flag is disabled in this environment — returned flag’s offValue |
Using targeting from each SDK
The targeting engine is the same across all SDKs — only the way you pass the user changes.
React (@flagify/react)
Pass options.user once to <FlagifyProvider> after the user is loaded by your auth layer. The Provider fetches all flag values already evaluated for that user and stores them in its local cache, so useFlag(), useFlagValue(), and useVariant() return the targeted values directly. Use key={user?.id ?? 'anonymous'} so the cache resyncs on login/logout. Full example in the React SDK guide.
Node.js (@flagify/node)
For multi-tenant servers (Express, Fastify, Next API routes), create the client once at startup with no user, and call await flagify.evaluate(key, user) per request — the example above. For single-user processes (CLIs, edge workers, RSC builds), pass options.user to the constructor and read from isEnabled() / getValue() after await ready(). Full details in the JavaScript SDK guide.