Targeting Rules
The targeting system provides sophisticated rule-based evaluation for feature flags. The TargetingController at /api/v1/targeting supports multi-dimensional targeting contexts including user attributes, environment details, device information, and custom properties. Rules are evaluated in priority order using a rich set of operators.
TargetingController
@RestController
@RequestMapping("/api/v1/targeting")
@Tag(name = "Feature Flag Targeting", description = "APIs for evaluating feature flags with targeting rules")
public class TargetingController {
private final TargetingEvaluatorService evaluatorService;
private final FeatureFlagService flagService;
}Evaluate Flags with Context
Evaluate one or more feature flags using a full targeting context. The context includes user attributes, environment details, device information, and custom properties.
Endpoint: POST /api/v1/targeting/evaluate
curl -X POST http://localhost:8888/api/v1/targeting/evaluate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000" \
-d '{
"flagKeys": ["ai.streaming-responses", "bi.new-chart-types"],
"user": {
"userId": "user-001",
"email": "alice@acme.com",
"username": "alice",
"groups": ["engineering", "beta-testers"],
"roles": ["admin", "data-analyst"],
"plan": "enterprise",
"isBetaUser": true,
"isInternal": false,
"createdAt": "2025-06-15T00:00:00Z"
},
"environment": {
"environment": "production",
"region": "us-east-1",
"country": "US"
},
"device": {
"appVersion": "2.5.0",
"platform": "web",
"deviceType": "desktop",
"browser": "chrome",
"ipAddress": "192.168.1.100",
"userAgent": "Mozilla/5.0..."
},
"custom": {
"department": "engineering",
"teamSize": 25,
"onboardingComplete": true
},
"metadata": {
"requestId": "req-abc-123"
}
}'Response:
{
"ai.streaming-responses": {
"flagKey": "ai.streaming-responses",
"flagId": "880e8400-...",
"enabled": true,
"value": "true",
"variant": null,
"reason": "TARGETING_MATCH",
"matchedRule": "beta-users-rule",
"metadata": { "ruleId": "rule-001", "priority": 1 }
},
"bi.new-chart-types": {
"flagKey": "bi.new-chart-types",
"flagId": "990e8400-...",
"enabled": false,
"value": "false",
"variant": null,
"reason": "FLAG_DISABLED",
"matchedRule": null,
"metadata": {}
}
}Quick Evaluation
A lightweight GET endpoint for simple evaluations with minimal context.
Endpoint: GET /api/v1/targeting/evaluate/{flagKey}
curl "http://localhost:8888/api/v1/targeting/evaluate/ai.streaming-responses?userId=user-001&environment=production" \
-H "Authorization: Bearer ${TOKEN}" \
-H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000"Targeting Context Structure
The targeting context is organized into four sections:
User Context
| Attribute | Type | Description |
|---|---|---|
userId | string | Unique user identifier |
email | string | User email address |
username | string | Username |
groups | string[] | Group memberships |
roles | string[] | Assigned roles |
plan | string | Subscription plan (free, pro, enterprise) |
isBetaUser | boolean | Whether user is a beta tester |
isInternal | boolean | Whether user is an internal employee |
createdAt | datetime | User creation timestamp |
Environment Context
| Attribute | Type | Description |
|---|---|---|
environment | string | Deployment environment (dev, staging, production) |
region | string | Cloud region (us-east-1, eu-west-1) |
country | string | Country code (ISO 3166-1) |
Device Context
| Attribute | Type | Description |
|---|---|---|
appVersion | semver | Application version |
platform | string | Platform (web, ios, android) |
deviceType | string | Device type (mobile, tablet, desktop) |
browser | string | Browser name |
ipAddress | string | Client IP address |
userAgent | string | User-Agent header value |
Custom Attributes
Any key-value pairs under the custom field. Access them in rules using custom.attributeName.
Available Operators
Endpoint: GET /api/v1/targeting/operators
| Operator | Display Name | Description |
|---|---|---|
EQUALS | Equals | Exact string/number match |
NOT_EQUALS | Not Equals | Does not match |
CONTAINS | Contains | Substring match |
NOT_CONTAINS | Not Contains | Does not contain substring |
STARTS_WITH | Starts With | Prefix match |
ENDS_WITH | Ends With | Suffix match |
MATCHES_REGEX | Matches Regex | Regular expression match |
IN | In | Value is one of the provided list |
NOT_IN | Not In | Value is not in the list |
GREATER_THAN | Greater Than | Numeric comparison |
GREATER_THAN_OR_EQUAL | Greater Than or Equal | Numeric comparison |
LESS_THAN | Less Than | Numeric comparison |
LESS_THAN_OR_EQUAL | Less Than or Equal | Numeric comparison |
EXISTS | Exists | Attribute is present |
NOT_EXISTS | Not Exists | Attribute is absent |
IS_TRUE | Is True | Boolean is true |
IS_FALSE | Is False | Boolean is false |
SEMVER_EQUALS | Semver Equals | Semantic version comparison |
SEMVER_GREATER_THAN | Semver Greater Than | Semantic version > |
SEMVER_LESS_THAN | Semver Less Than | Semantic version less-than |
BEFORE | Before | Date/time before |
AFTER | After | Date/time after |
Add a Targeting Rule
Add a targeting rule to a feature flag. Rules are evaluated in priority order (lowest priority number first).
Endpoint: POST /api/v1/targeting/flags/{flagId}/rules
curl -X POST http://localhost:8888/api/v1/targeting/flags/880e8400-e29b-41d4-a716-446655440000/rules \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000" \
-d '{
"name": "Beta Users",
"description": "Enable for beta testers in the US",
"priority": 1,
"enabled": true,
"conditions": [
{
"attribute": "isBetaUser",
"operator": "IS_TRUE",
"values": [],
"caseInsensitive": false,
"negate": false
},
{
"attribute": "country",
"operator": "EQUALS",
"values": ["US"],
"caseInsensitive": true,
"negate": false
}
],
"serveVariant": "true",
"rolloutPercentage": 100,
"bucketBy": "userId",
"metadata": { "team": "growth" }
}'Multiple conditions within a single rule are combined with AND logic. All conditions must match for the rule to apply.
Update a Targeting Rule
Endpoint: PUT /api/v1/targeting/flags/{flagId}/rules/{ruleId}
curl -X PUT http://localhost:8888/api/v1/targeting/flags/880e8400-e29b-41d4-a716-446655440000/rules/rule-001 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000" \
-d '{
"name": "Beta Users - All Regions",
"description": "Enable for beta testers in all regions",
"priority": 1,
"enabled": true,
"conditions": [
{
"attribute": "isBetaUser",
"operator": "IS_TRUE",
"values": [],
"caseInsensitive": false,
"negate": false
}
],
"serveVariant": "true",
"rolloutPercentage": 100,
"bucketBy": "userId"
}'Delete a Targeting Rule
Endpoint: DELETE /api/v1/targeting/flags/{flagId}/rules/{ruleId}
curl -X DELETE http://localhost:8888/api/v1/targeting/flags/880e8400-e29b-41d4-a716-446655440000/rules/rule-001 \
-H "Authorization: Bearer ${TOKEN}" \
-H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000"Reorder Targeting Rules
Change the evaluation priority of rules by providing the rule IDs in the desired order.
Endpoint: PUT /api/v1/targeting/flags/{flagId}/rules/reorder
curl -X PUT http://localhost:8888/api/v1/targeting/flags/880e8400-e29b-41d4-a716-446655440000/rules/reorder \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000" \
-d '["rule-003", "rule-001", "rule-002"]'Rule Evaluation Flow
For each flag:
1. Is flag enabled? No -> return DEFAULT
2. Iterate rules sorted by priority (ascending):
a. Is rule enabled? No -> skip
b. Evaluate all conditions (AND logic):
- Resolve attribute value from context
- Apply operator against condition values
- Apply case-insensitive and negate modifiers
c. All conditions match?
- Apply rollout percentage (hash(bucketBy) % 100)
- If within percentage -> return rule's serveVariant
d. No match -> try next rule
3. No rule matched -> return DEFAULTExample: Complex Targeting Rule
Target enterprise users in EMEA running app version 2.0+ on desktop:
{
"name": "Enterprise EMEA Desktop v2+",
"priority": 2,
"enabled": true,
"conditions": [
{
"attribute": "plan",
"operator": "EQUALS",
"values": ["enterprise"]
},
{
"attribute": "region",
"operator": "IN",
"values": ["eu-west-1", "eu-central-1", "eu-north-1"]
},
{
"attribute": "appVersion",
"operator": "SEMVER_GREATER_THAN",
"values": ["2.0.0"]
},
{
"attribute": "deviceType",
"operator": "EQUALS",
"values": ["desktop"]
}
],
"serveVariant": "true",
"rolloutPercentage": 50,
"bucketBy": "userId"
}