Config Management
Most customers manage configs through the Console UI. The management API is for infrastructure-as-code, CI/CD pipelines, setup scripts, and automated testing.
Management runs through the config surface of the smplkit client (SmplClient, or AsyncSmplClient for async) — client.config (Python, TypeScript, Java), client.Config (C#), or client.Config() (Go).
Create a Config
Create a new config with the factory method, set items via the typed setters, then call save() to persist. All mutations are local until save() is called.
import asyncio
from smplkit import AsyncSmplClient
async with AsyncSmplClient() as client:
user_service = client.config.new(
"user-service",
name="User Service",
description="Configuration for the user microservice.",
)
user_service.set_string("database.host", "localhost")
user_service.set_number("database.port", 5432)
user_service.set_number("database.pool_size", 5)
user_service.set_number("cache_ttl_seconds", 300)
user_service.set_boolean("enable_signup", True)
await user_service.save()Item Types
Every config item has one of four types, set via the matching typed setter:
| Type | Python / TS / Java / C# / Go | Notes |
|---|---|---|
STRING | set_string / setString / SetString | Plain strings |
NUMBER | set_number / setNumber / SetNumber | Integers and floats |
BOOLEAN | set_boolean / setBoolean / SetBoolean | true / false |
JSON | set_json / setJson / SetJson | Arbitrary JSON-serializable values |
The type is locked in once an item is created — calling set_string("max_retries", "5") and later set_number("max_retries", 5) is an error. Pick the right type at creation time.
Environment Overrides
Each item can have per-environment overrides. The runtime resolves the active environment's override; if none is set, the base value is used.
user_service.set_string(
"database.host",
"prod-users-rds.internal.acme.dev",
environment="production",
)
user_service.set_number("database.pool_size", 20, environment="production")
user_service.set_number("cache_ttl_seconds", 600, environment="production")
user_service.set_boolean("enable_signup", False, environment="production")
await user_service.save()Parent–Child Configs
A config can have a parent. Resolved values from the parent are inherited; values defined in the child override the parent. The hierarchy is at most two levels deep — the server rejects a parent that itself has a parent.
shared = client.config.new(
"common",
name="Common",
description="Shared defaults inherited by all services.",
)
shared.set_string("app_name", "Acme SaaS Platform")
shared.set_number("max_retries", 3)
await shared.save()
user_service = client.config.new(
"user-service",
name="User Service",
parent=shared, # or parent="common" — pass either an id or a Config
)
user_service.set_string("database.host", "localhost")
await user_service.save()Inheritance is opt-in — a config with no parent stands alone. The built-in common config is convenient as a parent for shared defaults, but it is not applied automatically.
List and Get Configs
configs = await client.config.list()
for cfg in configs:
parent_info = f" (parent: {cfg.parent})" if cfg.parent else " (root)"
print(f" {cfg.id}{parent_info}")
fetched = await client.config.get("user-service")
print(f"id={fetched.id}, name={fetched.name}, items={list(fetched.items.keys())}")Update a Config
Fetch a config, mutate via the typed setters, then save. Mutations accumulate locally; a single save() persists everything. Re-call set_string("key", value) (without environment=) to overwrite the base value, or with environment=... to overwrite the override.
fetched = await client.config.get("user-service")
fetched.description = "User microservice — updated description"
fetched.set_number("cache_ttl_seconds", 900, environment="production")
await fetched.save()Delete a Config
Children must be deleted before their parents, or the server will reject the parent delete with a ConflictError.
await client.config.delete("user-service")
# or: await fetched.delete()Sync Client (Python)
For synchronous applications (Django, Flask, CLI tools), use SmplClient instead of AsyncSmplClient. The API is identical but without await.
from smplkit import SmplClient
with SmplClient() as client:
cfg = client.config.new("my-service", name="My Service")
cfg.set_string("database.host", "localhost")
cfg.set_number("max_retries", 3)
cfg.save()
fetched = client.config.get("my-service")
fetched.set_number("max_retries", 5, environment="production")
fetched.save()
configs = client.config.list()
client.config.delete("my-service")Next Steps
- Config Runtime — Resolve values, subscribe to live updates, and react to changes
- Smpl Flags — Control feature rollouts with rules-based targeting
- API Reference — Config — Full REST API documentation

