Skip to content

Configs

Configs are the same primitive as flags, but with one of four types — string, number, boolean, json — and an optional JSON Schema for type=json configs. The endpoints mirror flags exactly.

The Config object (joined view):

json
{
  "id": "9f7a32b5-…",
  "projectId": "1234abcd-…",
  "envId": "5678ef01-…",
  "key": "checkout.max-items",
  "type": "number",
  "description": "Maximum items per cart.",
  "jsonSchema": null,
  "defaultValue": 100,
  "rules": [
    { "if": { "field": "plan", "$equals": "free" }, "value": 10 }
  ],
  "createdAt": "2026-04-12T10:00:00Z",
  "updatedAt": "2026-04-29T14:33:00Z"
}

jsonSchema is null for non-json types. For json configs, when set, every default value and rule value is validated against the schema at save time.


List configs

http
GET /api/v1/envs/{envId}/configs

Response: 200 → Config[] | 404 not_found

Get a config

http
GET /api/v1/envs/{envId}/configs/{key}

Response: 200 → ConfigErrors: 403 scope_denied, 404 not_found.


Create a config

http
POST /api/v1/projects/{id}/configs
Content-Type: application/json

{
  "key": "checkout.max-items",
  "type": "number",
  "defaultValue": 100,
  "rules": [],
  "description": "Maximum items per cart.",
  "jsonSchema": null
}

Bearer scope: write action. Session role: editor+.

Seeds env-state into every existing environment with the supplied values in one transaction.

type is one of string, number, boolean, json. defaultValue is required and must match the type. For type: json, null is allowed both as defaultValue and as any rule value; for other types, null is a 400 invalid_request.

Response: 201 → ConfigProjectErrors: 400 invalid_request, 403 scope_denied / approval_not_supported, 404 not_found, 409 key_collision.

Update config metadata

http
PATCH /api/v1/projects/{id}/configs/{key}
Content-Type: application/json
If-Match: W/"<configETag>"

{
  "description": "Max cart items (caps for free tier).",
  "jsonSchema": null
}

Bearer scope: write action. Session role: editor+.

Updates description and (for type: json) jsonSchema. Eval-irrelevant; doesn't rematerialize.

Response: 200 → ConfigProjectErrors: 400 invalid_request, 403 scope_denied / approval_not_supported, 404 not_found, 412 precondition_failed.

Delete a config

http
DELETE /api/v1/projects/{id}/configs/{key}

Bearer scope: delete action. Session role: editor+.

Cascades to every environment's state.

Response: 204 No ContentErrors: 403 scope_denied / approval_not_supported, 404 not_found.


Replace environment state

http
PUT /api/v1/envs/{envId}/configs/{key}/state
Content-Type: application/json
If-Match: W/"<envStateETag>"

{
  "defaultValue": 50,
  "rules": [
    { "if": { "field": "plan", "$equals": "free" }, "value": 10 }
  ]
}

Bearer scope: write action. Session role: editor+.

Full-replace of (rules, defaultValue) for this environment. Type validation per the config's declared type. Approval-gated tokens with rules-only deltas divert to the proposal flow (202 + Location header); mixed-dimension changes 403 with approval_not_supported.

Response: 200 → Config (joined view) | 202 → Proposal (approval-gated divert). Errors: 400 invalid_request, 403 scope_denied / approval_not_supported, 404 not_found, 412 precondition_failed.