Feature Flags
The Feature Flag system in the Config Service provides a complete feature management platform. The FeatureFlagController at /api/v1/feature-flags supports flag creation, evaluation with user targeting, percentage-based rollouts, allowed-user lists, batch evaluation, and tag-based organization.
FeatureFlagController
@RestController
@RequestMapping("/api/v1/feature-flags")
@Tag(name = "Feature Flags", description = "Feature flag management endpoints")
public class FeatureFlagController {
private final FeatureFlagService flagService;
private final ConfigMapper configMapper;
}Create a Feature Flag
Endpoint: POST /api/v1/feature-flags
curl -X POST http://localhost:8888/api/v1/feature-flags \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '{
"tenantId": "550e8400-e29b-41d4-a716-446655440000",
"flagKey": "ai.streaming-responses",
"displayName": "AI Streaming Responses",
"description": "Enable WebSocket streaming for AI conversation responses",
"flagType": "BOOLEAN",
"enabled": false,
"defaultValue": "false",
"rolloutPercentage": 0,
"tags": ["ai", "experimental", "performance"]
}'Response (201 Created):
{
"id": "880e8400-e29b-41d4-a716-446655440000",
"tenantId": "550e8400-e29b-41d4-a716-446655440000",
"flagKey": "ai.streaming-responses",
"displayName": "AI Streaming Responses",
"description": "Enable WebSocket streaming for AI conversation responses",
"flagType": "BOOLEAN",
"enabled": false,
"defaultValue": "false",
"rolloutPercentage": 0,
"allowedUsers": [],
"rules": [],
"tags": ["ai", "experimental", "performance"],
"createdAt": "2026-02-12T10:00:00Z",
"updatedAt": "2026-02-12T10:00:00Z"
}Check if Flag is Enabled
Simple boolean check without user context.
Endpoint: GET /api/v1/feature-flags/tenant/{tenantId}/key/{flagKey}/enabled
curl http://localhost:8888/api/v1/feature-flags/tenant/550e8400-e29b-41d4-a716-446655440000/key/ai.streaming-responses/enabled \
-H "Authorization: Bearer ${TOKEN}"Response:
{
"enabled": true
}Evaluate Flag for a User
Evaluate a feature flag in the context of a specific user. The evaluation considers the flag's enabled state, rollout percentage, allowed-user list, and targeting rules.
Endpoint: GET /api/v1/feature-flags/tenant/{tenantId}/key/{flagKey}/evaluate
curl "http://localhost:8888/api/v1/feature-flags/tenant/550e8400-e29b-41d4-a716-446655440000/key/ai.streaming-responses/evaluate?userId=user-001" \
-H "Authorization: Bearer ${TOKEN}"Response:
{
"flagKey": "ai.streaming-responses",
"enabled": true,
"reason": "ROLLOUT_PERCENTAGE",
"value": "true"
}Evaluation Reasons
| Reason | Description |
|---|---|
FLAG_DISABLED | Flag is globally disabled |
ALLOWED_USER | User is in the explicit allowed-user list |
TARGETING_RULE | A targeting rule matched the user context |
ROLLOUT_PERCENTAGE | User fell within the rollout percentage |
DEFAULT_VALUE | No rules matched; returning default |
Batch Evaluate Multiple Flags
Evaluate multiple flags for a single user in one request. This is the recommended approach for frontend applications that need to check many flags at once.
Endpoint: POST /api/v1/feature-flags/tenant/{tenantId}/evaluate-batch
curl -X POST "http://localhost:8888/api/v1/feature-flags/tenant/550e8400-e29b-41d4-a716-446655440000/evaluate-batch?userId=user-001" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '["ai.streaming-responses", "bi.new-chart-types", "ml.auto-feature-selection"]'Response:
{
"ai.streaming-responses": {
"flagKey": "ai.streaming-responses",
"enabled": true,
"reason": "ALLOWED_USER",
"value": "true"
},
"bi.new-chart-types": {
"flagKey": "bi.new-chart-types",
"enabled": false,
"reason": "FLAG_DISABLED",
"value": "false"
},
"ml.auto-feature-selection": {
"flagKey": "ml.auto-feature-selection",
"enabled": true,
"reason": "ROLLOUT_PERCENTAGE",
"value": "true"
}
}List Feature Flags
All flags for a tenant (paginated)
Endpoint: GET /api/v1/feature-flags/tenant/{tenantId}
curl "http://localhost:8888/api/v1/feature-flags/tenant/550e8400-e29b-41d4-a716-446655440000?page=0&size=20" \
-H "Authorization: Bearer ${TOKEN}"Enabled flags only
Endpoint: GET /api/v1/feature-flags/tenant/{tenantId}/enabled
Flags by tag
Endpoint: GET /api/v1/feature-flags/tenant/{tenantId}/tag/{tag}
curl http://localhost:8888/api/v1/feature-flags/tenant/550e8400-e29b-41d4-a716-446655440000/tag/experimental \
-H "Authorization: Bearer ${TOKEN}"Global flags
Endpoint: GET /api/v1/feature-flags/global
Enable / Disable a Flag
Toggle a flag on or off. This is the simplest way to control feature access.
Enable:
curl -X POST http://localhost:8888/api/v1/feature-flags/880e8400-e29b-41d4-a716-446655440000/enable \
-H "Authorization: Bearer ${TOKEN}"Disable:
curl -X POST http://localhost:8888/api/v1/feature-flags/880e8400-e29b-41d4-a716-446655440000/disable \
-H "Authorization: Bearer ${TOKEN}"Update Rollout Percentage
Gradually roll out a flag to a percentage of users. The percentage is applied deterministically using a hash of the user ID, so the same user always gets the same result.
Endpoint: PUT /api/v1/feature-flags/{id}/rollout
curl -X PUT "http://localhost:8888/api/v1/feature-flags/880e8400-e29b-41d4-a716-446655440000/rollout?percentage=25" \
-H "Authorization: Bearer ${TOKEN}"Recommended Rollout Strategy
Start at 5%
Enable the flag for internal users and 5% of the user base.
Monitor metrics
Watch error rates, latency, and user feedback for 24 hours.
Increase to 25%
If metrics are healthy, increase to 25%.
Increase to 50%
Continue monitoring for another 24 hours, then increase to 50%.
Full rollout
After successful 50% rollout, set to 100%.
Manage Allowed Users
Add or remove specific users from the flag's allowed list. Users on the allowed list always see the flag as enabled, regardless of rollout percentage.
Add users
Endpoint: POST /api/v1/feature-flags/{id}/users
curl -X POST http://localhost:8888/api/v1/feature-flags/880e8400-e29b-41d4-a716-446655440000/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '["user-001", "user-002", "user-003"]'Remove users
Endpoint: DELETE /api/v1/feature-flags/{id}/users
curl -X DELETE http://localhost:8888/api/v1/feature-flags/880e8400-e29b-41d4-a716-446655440000/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '["user-003"]'Update a Feature Flag
Endpoint: PUT /api/v1/feature-flags/{id}
curl -X PUT http://localhost:8888/api/v1/feature-flags/880e8400-e29b-41d4-a716-446655440000 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TOKEN}" \
-d '{
"displayName": "AI Streaming Responses (v2)",
"description": "Updated streaming implementation with chunked transfer",
"rolloutPercentage": 50,
"tags": ["ai", "stable", "performance"]
}'Delete a Feature Flag
Endpoint: DELETE /api/v1/feature-flags/{id}
curl -X DELETE http://localhost:8888/api/v1/feature-flags/880e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer ${TOKEN}"Response: 204 No Content
Deleting a feature flag immediately removes it from all evaluation paths. Any service checking this flag will receive the default value. Consider disabling the flag first and monitoring for issues before deleting.
Flag Evaluation Flow
The internal evaluation logic follows this decision tree:
Is flag.enabled == false?
YES -> return DEFAULT_VALUE
Is userId in flag.allowedUsers?
YES -> return ALLOWED_USER (enabled)
Do any targeting rules match?
YES -> return TARGETING_RULE (rule value)
Is rolloutPercentage > 0?
hash(flagKey + userId) % 100 < rolloutPercentage?
YES -> return ROLLOUT_PERCENTAGE (enabled)
NO -> return DEFAULT_VALUE
return DEFAULT_VALUEError Codes
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | INVALID_FLAG_TYPE | Invalid flag type specified |
| 404 | FLAG_NOT_FOUND | Feature flag does not exist |
| 409 | FLAG_KEY_EXISTS | Duplicate flag key for tenant |
| 422 | INVALID_PERCENTAGE | Rollout percentage must be 0-100 |