Skip to content

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, or null. 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 ​