# Flag Definition Schema & Supported Types

> The exampleFlags.json contract: required fields (key, type, defaultValue), optional fields (name, description), and the four supported type values (BOOLEAN, STRING, NUMBER, JSON). How each type maps to a typed value field in the FeatureHub API (valueBoolean, valueString, valueNumber, valueJson). The special BOOLEAN unlock step. How to add, remove, or change seed flags and rebuild. The seven seed flags shipped by default.

- Repository: vtex/dk-flags
- GitHub: https://github.com/vtex/dk-flags
- Human wiki: https://grok-wiki.com/public/wiki/vtex-dk-flags-0a8c140c3cfa
- Complete Markdown: https://grok-wiki.com/public/wiki/vtex-dk-flags-0a8c140c3cfa/llms-full.txt

## Source Files

- `docker/exampleFlags.json`
- `docker/features.js`
- `docker/flags.js`
- `specs/dk-flags-party-server-image.md`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:

- [docker/exampleFlags.json](docker/exampleFlags.json)
- [docker/features.js](docker/features.js)
- [docker/flags.js](docker/flags.js)
- [specs/dk-flags-party-server-image.md](specs/dk-flags-party-server-image.md)
</details>

# Flag Definition Schema & Supported Types

`docker/exampleFlags.json` is the single source of truth for the seed feature flags baked into the `dk-flags-party-server` Docker image at build time. Every entry in this file is read, validated, and submitted to the FeatureHub Management REST API during `docker build`, so the resulting image starts with all flags already created and their default values set.

Understanding the schema is the prerequisite for adding, removing, or changing flags, because any modification requires a Docker image rebuild to take effect. This page documents every field the schema accepts, the four supported type values, how each type maps to a typed API field, and the special unlock step that applies only to `BOOLEAN` flags.

---

## Schema Fields

Each element of the top-level JSON array represents one feature flag. The schema supports five fields.

| Field | Required | Type | Default when omitted |
|---|---|---|---|
| `key` | Yes | `string` | — |
| `type` | Yes | `"BOOLEAN" \| "STRING" \| "NUMBER" \| "JSON"` | — |
| `defaultValue` | Yes | matches `type` | — |
| `name` | No | `string` | falls back to `key` |
| `description` | No | `string` | falls back to `"Feature flag: ${key}"` |

Sources: [docker/features.js:3-7]()

### Required Fields

**`key`** — the FeatureHub flag key. This is the identifier used in every subsequent API call (`POST /mr-api/application/{id}/features`, `PUT /mr-api/features/{envId}/feature/{key}`). It must be unique within the application.

**`type`** — one of the four string literals `BOOLEAN`, `STRING`, `NUMBER`, or `JSON`. This value is passed directly as `valueType` in the feature-creation payload and determines which typed value field is populated during the value-set step.

**`defaultValue`** — the initial value baked into the environment at build time. The JavaScript type must match the declared `type`: a native boolean for `BOOLEAN`, a native string for `STRING`, a native number for `NUMBER`, and a JSON-serializable object or array for `JSON`.

### Optional Fields

**`name`** — a human-readable display name shown in the FeatureHub Management UI. When omitted, the flag `key` is used as the name.

**`description`** — a short description rendered in the UI. When omitted, the setup pipeline generates `"Feature flag: ${key}"`.

Sources: [docker/features.js:3-8]()

---

## Supported Types and API Mapping

When `setFeatureValue` submits a `PUT /mr-api/features/{environmentId}/feature/{key}` request, it constructs a value payload with four typed fields — only one of which is non-null depending on the flag's `type`. The other three fields are explicitly set to `null`.

```js
// docker/features.js
const valueData = {
    key: flagKey,
    locked: false,
    valueString: flagType === 'STRING'  ? flag.defaultValue : null,
    valueNumber: flagType === 'NUMBER'  ? flag.defaultValue : null,
    valueBoolean: flagType === 'BOOLEAN' ? flag.defaultValue : null,
    valueJson:   flagType === 'JSON'    ? JSON.stringify(flag.defaultValue) : null,
    version: version
};
```

Sources: [docker/features.js:73-81]()

| `type` in schema | Populated API field | JavaScript value |
|---|---|---|
| `BOOLEAN` | `valueBoolean` | native `true` / `false` |
| `STRING` | `valueString` | string literal |
| `NUMBER` | `valueNumber` | integer or float |
| `JSON` | `valueJson` | `JSON.stringify(defaultValue)` — object or array |

A flag whose `type` is not one of these four literals will cause all four value fields to be `null` in the payload. The FeatureHub API will accept the feature-creation step but the value will not be meaningful.

Sources: [specs/dk-flags-party-server-image.md:334]()

---

## The BOOLEAN Unlock Step

FeatureHub locks newly created feature flags by default. For `BOOLEAN` flags specifically, a dedicated unlock call is required **before** the value-set call; without it the environment cannot record the boolean default.

The pipeline detects the flag type and branches:

```js
// docker/features.js (createAllFeatures)
if (flag.type === 'BOOLEAN') {
    await unlockBooleanFlag(applicationId, flag.key, environmentId, flag.defaultValue, version, token);
    version++;
}
// Set feature value (all types)
const valueResult = await setFeatureValue(environmentId, flag, version, token);
```

Sources: [docker/features.js:119-127]()

The `unlockBooleanFlag` function issues a `PUT /mr-api/application/{id}/feature-environments/{key}` with `locked: false` and the `valueBoolean` field set to the flag's `defaultValue`. This is the only type-conditional branch in the whole pipeline; all other types skip directly to the value-set call, which itself sets `locked: false` in the body.

Sources: [docker/features.js:43-49](), [specs/dk-flags-party-server-image.md:215-217]()

```text
BOOLEAN flag lifecycle during build
────────────────────────────────────
 POST /mr-api/application/{id}/features        ← create (locked by default)
 PUT  /mr-api/application/{id}/feature-environments/{key}  ← unlock + set valueBoolean
 PUT  /mr-api/features/{envId}/feature/{key}   ← set typed value (locked:false)

STRING / NUMBER / JSON flag lifecycle
───────────────────────────────────────
 POST /mr-api/application/{id}/features        ← create
 PUT  /mr-api/features/{envId}/feature/{key}   ← set typed value (locked:false)
```

---

## The Seven Default Seed Flags

`docker/exampleFlags.json` ships seven seed flags that cover every supported type, including two distinct `NUMBER` shapes (integer and float) and three distinct `JSON` shapes (flat object, nested object, and array).

| `key` | `type` | `defaultValue` |
|---|---|---|
| `booleanConf` | `BOOLEAN` | `true` |
| `stringConf` | `STRING` | `"myStringConf"` |
| `intConf` | `NUMBER` | `10` |
| `floatConf` | `NUMBER` | `1.5` |
| `jsonConf` | `JSON` | `{ "data": "test", "evaluation": true }` |
| `objectConf` | `JSON` | nested object with string, int, bool, and array attributes |
| `stringArrayConf` | `JSON` | `["stringValue1", "stringValue2", "stringValue3"]` |

Sources: [docker/exampleFlags.json:1-54]()

The `JSON` type accepts any JSON-serializable value — a flat object, a deeply nested object, or a plain array — because `setFeatureValue` calls `JSON.stringify(flag.defaultValue)` before sending the payload. The `objectConf` entry demonstrates nested objects with mixed attribute types, while `stringArrayConf` demonstrates that a bare array is equally valid.

---

## Loading the File at Build Time

`docker/flags.js` exposes a single `loadFlags()` function that searches four candidate paths for the file and parses it:

```js
const possiblePaths = [
    path.join(__dirname, 'exampleFlags.json'),
    path.join(process.cwd(), 'exampleFlags.json'),
    '/app/exampleFlags.json',
    './exampleFlags.json'
];
```

Sources: [docker/flags.js:5-10]()

The first path that exists and parses successfully is used. If none is found, `loadFlags()` returns `null` and `setup.js` exits with a "No flags to create" message — the image still builds successfully, but no flags are seeded. This behavior makes a missing file a recoverable situation rather than a hard build failure.

---

## Adding, Removing, or Changing Seed Flags

Changes to `docker/exampleFlags.json` are **not** hot-reloaded. The file is read once at `docker build` time; a running container cannot pick up edits. The workflow is:

1. Edit `docker/exampleFlags.json` — add, remove, or modify entries following the schema above.
2. Rebuild the image: `docker build -f docker/Dockerfile -t dk-flags-party-server .`
3. Start a new container from the updated image.

A flag present in a running container but removed from `exampleFlags.json` will remain in FeatureHub after rebuild (there is no reconciliation or deletion step; `409 Conflict` on creation is silently treated as "already exists" / skipped). If a flag that already exists in FeatureHub is encountered, the creation call returns `409` and the pipeline counts it as `skipped` rather than `error`, keeping the run idempotent.

Sources: [specs/dk-flags-party-server-image.md:39-42](), [docker/features.js:26-29]()

---

## Summary

`docker/exampleFlags.json` is a JSON array of flag definitions. Each entry requires `key`, `type`, and `defaultValue`; `name` and `description` are optional. The four legal `type` values are `BOOLEAN`, `STRING`, `NUMBER`, and `JSON`, each mapping to a dedicated typed field in the FeatureHub value-set API call (`valueBoolean`, `valueString`, `valueNumber`, `valueJson`). `JSON` typed flags accept any JSON-serializable value and are stringified before submission. `BOOLEAN` flags require an extra unlock API call before the value-set call, which is the only type-conditional branch in the pipeline. The repository ships seven seed flags demonstrating all types. Modifying the seed set requires editing the file and rebuilding the Docker image.
