Overview
Feature flags provide kill-switch and gradual rollout capabilities. Flags can be targeted to specific segments, companies, or users.
Base URL
/api/admin/config/featuresNOTE
These endpoints require authentication with a Super Admin account.
Endpoints
List Feature Flags
http
GET /api/admin/config/featuresReturns all feature flags with their associated segments.
Response 200 OK
json
[
{
"id": "feedback_widget",
"name": "Feedback Widget",
"description": "Show feedback widget in app",
"type": "boolean",
"isEnabled": true,
"status": "development",
"companyOverrides": [],
"userOverrides": ["marc.montecalvo@gmail.com"],
"segments": [
{
"id": "ffs-123",
"segmentId": "seg-456",
"segment": {
"id": "seg-456",
"name": "beta-testers"
}
}
],
"createdAt": "2025-12-01T00:00:00.000Z",
"updatedAt": "2025-12-10T00:00:00.000Z"
}
]Create Feature Flag
http
POST /api/admin/config/featuresRequest Body
json
{
"id": "new_dashboard_v2",
"name": "New Dashboard V2",
"description": "Redesigned dashboard layout",
"isEnabled": false,
"type": "boolean",
"status": "development"
}| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | ✅ | - | Code-defined ID (snake_case recommended) |
name | string | ✅ | - | Human-readable name |
description | string | ❌ | - | Feature description |
isEnabled | boolean | ❌ | false | Global kill switch |
type | string | ❌ | "boolean" | Flag type |
status | string | ❌ | "development" | development, active, deprecated |
Response 200 OK - Created flag object
Update Feature Flag
http
PUT /api/admin/config/features/:idParameters
| Name | Type | Description |
|---|---|---|
id | string | Feature flag ID |
Request Body - All fields optional
json
{
"isEnabled": true,
"description": "Updated description",
"status": "active",
"companyOverrides": ["company-123"],
"userOverrides": ["user@example.com"]
}| Field | Type | Description |
|---|---|---|
isEnabled | boolean | Global kill switch |
description | string | Feature description |
status | string | Status: development, active, deprecated |
companyOverrides | string[] | Company IDs that always have access |
userOverrides | string[] | User IDs or emails that always have access |
Response 200 OK - Updated flag object
Attach Segment to Flag
http
POST /api/admin/config/features/:id/segmentsParameters
| Name | Type | Description |
|---|---|---|
id | string | Feature flag ID |
Request Body
json
{
"segmentId": "seg-456"
}Response 200 OK
json
{
"id": "ffs-789",
"featureFlagId": "feedback_widget",
"segmentId": "seg-456"
}Detach Segment from Flag
http
DELETE /api/admin/config/features/:id/segments/:segmentIdParameters
| Name | Type | Description |
|---|---|---|
id | string | Feature flag ID |
segmentId | string | Segment ID to detach |
Response 200 OK
json
{
"success": true
}Access Evaluation Logic
When checking feature access, the system evaluates in this order:
- Direct Overrides - If user/company is in overrides array → ALLOW
- Segment Membership - If user/company is in any attached active segment → ALLOW
- Global Switch - If
isEnabledis true → ALLOW - Otherwise → DENY
typescript
import { isFeatureEnabled } from '../services/featureFlags.service.js';
const hasAccess = await isFeatureEnabled('my_feature', {
companyId: 'company-123',
userId: 'user-456',
email: 'user@example.com'
});