Feature Flags
Create & Manage Flags
Create, update, enable/disable, and delete feature flags via the REST API or the console.
Create a feature flag
Choose a flag type
| Type | Use case | Variant values |
|---|---|---|
boolean | On/off toggles | true / false |
string | Named modes, copy variants | Strings |
number | Limits, ratios, sizes | Numbers |
object | Configuration, A/B testing | Structured JSON payloads |
Define variants
Every flag needs at least one variant and a default. Each variant is { "name", "description", "value" }; the default is { "type": "basic", "value": "<variant name>" }. See Variants for details.
Create via the API or the console
curl -X POST http://localhost:6060/api/feature-flags \
-H "Authorization: Bearer ksh_xxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Table Reservations",
"slug": "table-reservations-enabled",
"description": "Gates the table reservation UI.",
"type": "boolean",
"enabled": true,
"event_name": "table-reservations-enabled.evaluated",
"metadata": {},
"variants": [
{ "name": "on", "description": "Reservation UI visible", "value": true },
{ "name": "off", "description": "Reservation UI hidden", "value": false }
],
"default_variant": { "type": "basic", "value": "off" },
"targetings": []
}'The response is 201 Created with the flag. Creating a slug that already exists in the organization is refused with 409 Conflict.
Open Feature Flags and click New Feature Flag. The page walks four steps — Basic Information, Variants Configuration, Default Variant and Targeting Rules — with the Enabled switch in its header, and ends on Create Feature Flag.
A boolean flag starts with two variants named true and false, whose values are locked: rename them — to on and off, as in the API example — if your code expects other names. Typing the name fills in the slug, so set a slug of your own after the name.
Flag properties
| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name (e.g. "Table Reservations") |
slug | string | No | URL-safe identifier (^[a-z0-9][a-z0-9-]*[a-z0-9]$, 2–100 characters), generated from the name with a random suffix if omitted. Unique per organization. |
description | string | Yes | Free text; may be null |
type | string | Yes | boolean, string, number or object |
enabled | boolean | Yes | Master switch — when false, always returns the default variant |
event_name | string | Yes | A label stored with the flag; evaluation events do not carry it |
variants | object[] | Yes | The possible values the flag can resolve to — each { "name", "description", "value" } |
default_variant | object | Yes | Served when no rule matches or the flag is disabled — { "type": "basic", "value": "<variant name>" }, or a rollout_percentage / rollout_date form (Variants) |
targetings | object[] | Yes | Ordered rules for per-instance resolution — [] for none (Targeting rules) |
metadata | object | Yes | Free-form metadata attached to the flag — {} for none |
Update a flag
PUT /feature-flags/{slug} replaces the whole definition — there is no partial update, so send every property, changed or not. The slug is the exception: send one to rename the flag, omit it to keep the current one — and a rename changes the key your code evaluates. The response is 204 No Content:
curl -X PUT http://localhost:6060/api/feature-flags/table-reservations-enabled \
-H "Authorization: Bearer ksh_xxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Table Reservations",
"slug": "table-reservations-enabled",
"description": "Gates the table reservation UI.",
"type": "boolean",
"enabled": true,
"event_name": "table-reservations-enabled.evaluated",
"metadata": {},
"variants": [
{ "name": "on", "description": "Reservation UI visible", "value": true },
{ "name": "off", "description": "Reservation UI hidden", "value": false }
],
"default_variant": { "type": "basic", "value": "off" },
"targetings": [
{
"type": "basic",
"name": "Premium license",
"rule": "__kaiten.license.familySlug == \"premium\"",
"variant": "on"
}
]
}'Open the flag and click Configure. The four steps of the creation page become tabs, with the Enabled switch in the header; Update Feature Flag sends the whole definition. The flag's own tabs are read-only: editing happens in Configure.
Enable / disable
The enabled field acts as a master switch:
enabled | Behavior |
|---|---|
true | Targeting rules are evaluated; matching rules return their variant |
false | All evaluations return the default_variant with reason DISABLED — no rule is evaluated |
This lets you disable a flag without deleting its configuration.
# Disable a flag: read it, flip `enabled`, and PUT the whole definition back
curl -s http://localhost:6060/api/feature-flags/table-reservations-enabled \
-H "Authorization: Bearer ksh_xxx" \
| jq 'del(.id) | .enabled = false' \
| curl -X PUT http://localhost:6060/api/feature-flags/table-reservations-enabled \
-H "Authorization: Bearer ksh_xxx" \
-H "Content-Type: application/json" \
-d @-On Feature Flags, switch to the List view: each card carries a switch, which asks for confirmation — the change applies to every evaluation of the flag, immediately. The Table view shows the state without a switch. On the flag itself, click Configure, use the Enabled switch in the header, and click Update Feature Flag.
Delete a flag
curl -X DELETE http://localhost:6060/api/feature-flags/table-reservations-enabled \
-H "Authorization: Bearer ksh_xxx"The console has no delete action for a flag: deleting one is an API call.
Deleting a flag is permanent. Any application code evaluating this flag receives an error response after deletion. Remove flag evaluation from your codebase first.
List and filter
curl http://localhost:6060/api/feature-flags \
-H "Authorization: Bearer ksh_xxx"The response is a page — items carrying each flag's full definition (variants, targetings, enabled state), hasMore, and a nextCursor to pass back as ?cursor= for the next page. limit defaults to 50 and caps at 200.
In the console
Flag list
The Feature Flags page shows four counters — Total Flags, Enabled, Disabled and With Targeting — above the flags, in one of two views:
- Table — each flag's name and slug, its type, its enabled state, its variants, its targeting rules, and its metadata.
- List — one card per flag, with its switch and three actions: Try it, View and Configure.
Flag page
A flag's page is read-only. Its header carries the flag's state and type, and two actions, Try it and Configure:
| Tab | Content |
|---|---|
| Overview | Type, event name, slug, ID, status and owner; the default variant strategy; metadata |
| Variants | Each variant with its description, a preview of its payload, and whether the default uses it |
| Targeting | The rules in evaluation order, and the fallback step |
| Try it history | What Try it returned in this browser session — nothing there is stored |
Try it, on the flag's page and on each card of the List view, evaluates the flag through the API's OFREP endpoint, for a JSON context that must include targetingKey, and shows the reason, the value, the variant and the metadata of the response. It is an evaluation like any other, recorded in the organization's audit trail. Run from the flag's page, it is also added to Try it history for the session.
Targeting Rules
CEL rules decide which variant an instance receives — what a rule can read under __kaiten, how rules are ordered and evaluated, percentage and date rollouts, and the three operations that check a rule before it is saved.
OpenFeature & OFREP
Evaluate feature flags using the standard OpenFeature Remote Evaluation Protocol — compatible with any OpenFeature SDK.