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

  1. You set targeting rules on a flag-environment (e.g. checkout-redesign in development)
  2. The SDK calls the evaluate endpoint with the user’s context
  3. If the flag is disabled, rules are skipped entirely and the flag’s offValue is returned
  4. If the flag is enabled, rules are checked in priority order — the first match wins
  5. If no rule matches (or no rules are defined), the flag’s defaultValue is 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 matches
  • rolloutPercentage — 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

OperatorDescriptionExample
equalsExact matchplan equals "pro"
not_equalsInverse matchplan not_equals "free"
containsSubstring matchemail contains "@acme.com"
not_containsInverse substringemail not_contains "test"
starts_withPrefix matchemail starts_with "admin"
ends_withSuffix matchemail ends_with ".edu"
inValue in listcountry in ["US", "CA", "UK"]
not_inValue not in listcountry not_in ["CN", "RU"]
gtGreater than (numeric)age gt 18
ltLess 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"
}
ReasonDescription
targeting_ruleA targeting rule with conditions/segment matched
rolloutA rollout percentage rule matched
no_matchTargeting rules exist but none matched — returned flag’s defaultValue
defaultNo targeting rules defined — returned flag’s defaultValue
disabledFlag 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.