Flags Management
Most customers create and configure flags via the Console UI. The management API is for infrastructure-as-code, CI/CD pipelines, setup scripts, and automated testing. Management operations are stateless HTTP calls — no WebSocket or start() needed.
Management lives at client.manage.flags.* on the runtime client. For setup scripts and admin tooling, construct a standalone SmplManagementClient directly — same surface, no WebSocket or metrics overhead.
Create a Boolean Flag
The flag is created locally — nothing hits the server until save() is called.
import asyncio
from smplkit import AsyncSmplManagementClient
async with AsyncSmplManagementClient() as manage:
checkout = manage.flags.new_boolean_flag(
"checkout-v2",
default=False,
description="Controls rollout of the new checkout experience.",
)
await checkout.save()Create a String Flag
String flags can be constrained (closed set of allowed values) or unconstrained. The values parameter defines the allowed set — the Console UI shows a dropdown for value selection.
from smplkit import FlagValue
banner = manage.flags.new_string_flag(
"banner-color",
default="red",
name="Banner Color",
description="Controls the banner color shown to users.",
values=[
FlagValue(name="Red", value="red"),
FlagValue(name="Green", value="green"),
FlagValue(name="Blue", value="blue"),
],
)
await banner.save()Create a Number Flag
Omit values for an unconstrained number flag — any number is valid. Useful for tunables like thresholds, retry counts, and timeouts.
retries = manage.flags.new_number_flag(
"max-retries",
default=3,
description="Maximum number of API retries before failing.",
)
await retries.save()Create a JSON Flag
JSON flags serve complex configuration objects. Like string flags, they can be constrained to a set of predefined values.
from smplkit import FlagValue
theme = manage.flags.new_json_flag(
"ui-theme",
default={"mode": "light", "accent": "#0066cc"},
description="Controls the UI theme configuration.",
values=[
FlagValue(name="Light", value={"mode": "light", "accent": "#0066cc"}),
FlagValue(name="Dark", value={"mode": "dark", "accent": "#66ccff"}),
FlagValue(name="High Contrast",
value={"mode": "dark", "accent": "#ffffff"}),
],
)
await theme.save()Targeting Rules
Rules define conditions under which a flag serves a specific value. Build rules with the fluent Rule builder. The environment is required — it scopes the rule to a single environment. Multiple .when() calls within a rule are AND-ed together.
Supported operators: ==, !=, >, <, >=, <=, in, contains. Python and TypeScript also expose them as Op.EQ, Op.NEQ, etc.
Single-condition rule
from smplkit import Op, Rule
checkout.add_rule(
Rule("Enable for enterprise users", environment="staging")
.when("user.plan", Op.EQ, "enterprise")
.serve(True)
)
await checkout.save()Multi-condition rule
checkout.add_rule(
Rule("Enable for enterprise users in US region", environment="staging")
.when("user.plan", Op.EQ, "enterprise")
.when("account.region", Op.EQ, "us")
.serve(True)
)
await checkout.save()Per-Environment Defaults and Kill Switch
Each environment has its own default value (falls back to the flag default if unset) and an enabled flag (the kill switch). When enabled is false, rules are skipped and the per-environment default is served.
# Per-environment default — wins over the base flag default in this env.
checkout.set_default(False, environment="production")
# Kill switch — bypass all targeting rules in production.
checkout.disable_rules(environment="production")
checkout.enable_rules(environment="staging")
# Revert overrides:
checkout.clear_default(environment="production")
checkout.clear_rules(environment="staging") # remove every rule on staging
await checkout.save()List and Get Flags
flags = await manage.flags.list()
for f in flags:
envs = list(f.environments.keys()) if f.environments else []
print(f" {f.id} ({f.type}) — default={f.default}, environments={envs}")
fetched = await manage.flags.get("checkout-v2")
print(f"id={fetched.id}, type={fetched.type}, default={fetched.default}")Update a Flag
Fetch a flag, mutate, save. Constrained-flag values can be added with add_value/addValue/AddValue and removed wholesale with clear_values/clearValues/ClearValues (which makes the flag unconstrained).
banner.add_value("Purple", "purple")
banner.default = "blue"
banner.description = "Controls the banner color — updated"
banner.add_rule(
Rule("Purple for enterprise users", environment="production")
.when("user.plan", Op.EQ, "enterprise")
.serve("purple")
)
await banner.save()Delete a Flag
await manage.flags.delete("checkout-v2")
# or: await checkout.delete()Sync Client (Python)
For synchronous applications, use SmplManagementClient instead of AsyncSmplManagementClient:
from smplkit import SmplManagementClient, Op, Rule
with SmplManagementClient() as manage:
flag = manage.flags.new_boolean_flag("my-flag", default=False)
flag.save()
flag = manage.flags.get("my-flag")
flag.add_rule(
Rule("...", environment="staging")
.when("user.plan", Op.EQ, "enterprise")
.serve(True)
)
flag.save()
manage.flags.delete("my-flag")From a runtime client
Inside a service that already holds a SmplClient, use client.manage.flags.* to avoid juggling two clients:
from smplkit import SmplClient, Rule, Op
with SmplClient(environment="production", service="my-service") as client:
banner = client.manage.flags.get("banner-color")
banner.add_rule(
Rule("Red for small companies", environment="staging")
.when("account.employee_count", Op.LT, 50)
.serve("red")
)
banner.save()The same pattern applies in every SDK: client.manage.flags.* (Python/TS/C#), client.Manage().Flags() (Go), client.manage().flags (Java).
Next Steps
- Flags Runtime — Declare flags in code, evaluate locally, react to changes
- Smpl Logging — Control log verbosity without redeploying
- API Reference — Flags — Full REST API documentation

