# CDN JSON format

> The schema of the file served from the edge.

This is the static file the SDKs read, served at
`https://cdn.switchbox.dev/{sdk_key}/flags.json`. You never have to parse it
yourself — the SDK does — but it's documented here for debugging and for anyone
building their own client. It's a public, additive-only contract.

## The shape

```json
{
  "version": "2026-04-07T12:00:00Z",
  "flags": {
    "new_checkout": {
      "enabled": true,
      "rollout_pct": 30,
      "flag_type": "string",
      "default_value": "Shop",
      "enabled_value": "Buy now",
      "rules": [
        {
          "conditions": [
            { "attribute": "country", "operator": "equals", "value": "US" },
            { "attribute": "device", "operator": "equals", "value": "mobile" }
          ]
        },
        {
          "conditions": [
            { "attribute": "plan", "operator": "equals", "value": "enterprise" }
          ]
        }
      ]
    }
  }
}
```

The `rules` above read as *(country = US **and** device = mobile) **or** (plan =
enterprise)*.

## Fields

| Field | Type | Notes |
|---|---|---|
| `version` | string | Timestamp of this config. Changes on every publish; SDKs use it to detect a new version. |
| `flags` | object | Map of flag key → flag object. |

Each flag object:

| Field | Type | Notes |
|---|---|---|
| `enabled` | boolean | Whether the flag is on in this environment. |
| `rollout_pct` | number | 0–100. `100` means serve-everyone (see [rollouts](/docs/concepts/rollouts)). |
| `flag_type` | string | `boolean`, `string`, `number`, or `json`. |
| `default_value` | any | Value for off / unmatched users. |
| `enabled_value` | any | **Optional.** Value for matched / in-rollout users. Omitted when unset; a boolean flag's on-value is implicitly `true`. |
| `rules` | array | Targeting, as **groups** of conditions (see below). |

A condition is `{ "attribute", "operator", "value" }` — see
[Rule operators](/docs/reference/operators).

## Two-level rules (DNF)

`rules` is a list of **groups** (OR'd); each group has a `conditions` list
(AND'd). Segments you attached in the dashboard are **expanded inline** at publish
time — the file never contains a segment reference, only plain conditions. So the
SDK needs no segment lookups.

## What's deliberately not here

- **No flag-level values or per-environment overrides.** Those are control-plane
  concepts; the backend resolves each environment's value pair before publishing,
  so this file already carries one resolved `default_value`/`enabled_value` per
  environment.
- **`enabled_value` is omitted when unset**, so boolean and pre-variations
  configs are byte-identical to older ones.

## Backward compatibility

The wire format is **additive-only** — new optional fields may appear, but
existing ones won't change meaning, so older SDKs keep working. SDKs also still
accept the legacy flat `rules` shape (`[{ attribute, operator, value }]`) from
pre-DNF configs, treating each entry as a single-condition group; configs are
rewritten to the group shape on their next publish.

## Next

- [How it works](/docs/concepts/how-it-works) — how this file is published and
  fetched.
- [Evaluation order](/docs/reference/evaluation-order) — how the SDK reads it.
