Appearance
Configs
A dynamic config is the same primitive as a flag, but the value is a runtime constant that's typically not on/off. Examples:
checkout.max-items— a number cap that ops can change without a deploy.support.contact-email— a string the marketing team owns.pricing.intro-discount— a JSON blob describing a tiered discount table.
Configs ship four types:
string— plain UTF-8 strings.number— JSON numbers (integers and finite floats).boolean—true/false. (For a true on/off, a flag is usually clearer; this exists for cases where the same key is sometimes a literal config value.)json— arbitrary JSON: object, array, string, number, boolean, ornull. Optionally validated against a JSON Schema you provide.
Why configs and flags are separate
The wire surface and rule engine are identical between flags and configs. The split exists because the two have different mental models for users:
- A flag is "should we do this thing?" — typically read once at a decision point.
- A config is "what's the current value of this knob?" — typically read on every request.
Keeping them separate in the dashboard, the audit log, and the SDKs lets you scan a list of flags without drowning in numeric configuration, and vice versa.
JSON configs and schemas
For type: json configs, you can attach a JSON Schema to the project-scope identity:
json
{
"type": "object",
"properties": {
"tiers": {
"type": "array",
"items": { "type": "object", "required": ["minItems", "discount"] }
}
},
"required": ["tiers"]
}When set, every write to the config — default value or rule value — is validated against the schema at save time. The dashboard surfaces validation errors inline; the API returns a 400 invalid_request with fields populated.
Default value semantics for json configs: null is a valid value (and the only type for which null is allowed). For string, number, and boolean configs, the default and any rule value must be a non-null instance of the declared type.
Editing a config
Same shape as flags:
- Project-scope metadata —
PATCH /api/v1/projects/{id}/configs/{key}. Update description and JSON Schema. - Per-environment state —
PUT /api/v1/envs/{envId}/configs/{key}/state. Full-replace of(rules, defaultValue). - Delete —
DELETE /api/v1/projects/{id}/configs/{key}. Cascades.
Reading a config
http
GET /api/v1/envs/{envId}/configs/{key}Returns the joined view: type, schema (if any), description, plus this env's rules and default.
For SDK consumers, the typical pattern is evaluate with keys: ['*'] once at startup (and on every SSE update once that lands), caching the resolved values in process memory. The config value is just the resolved evaluations[key].value from that response.
See also
- Rules and contexts — rule shape, conditions, percentage rollouts.
- Evaluation — how to read configs from your code.
- API reference: Configs — every endpoint, request shape, response shape.