# Flags & flag types

> Boolean, string, and number flags, default values, and keys.

A flag is a named switch your code reads at runtime. Each flag has a **key**, a
**type**, and one or more **values**, and exists once per project (with separate
targeting per environment).

## Flag keys

A key is the string you reference in code. Keys must be
`lowercase_snake_case` — they match the pattern `^[a-z][a-z0-9_]*$`:

- Good: `new_checkout`, `search_v2`, `dark_mode`, `max_results`
- Rejected: `NewCheckout`, `new-checkout`, `1_flag`

A key is unique within a project. Pick descriptive names — you'll accumulate
dozens of flags, and clear names prevent confusion.

## Flag types

Switchbox supports four value types:

| Type | What it returns | Read with |
|---|---|---|
| `boolean` | `true` / `false` | `enabled()` |
| `string` | any string | `getValue()` |
| `number` | any number | `getValue()` |
| `json` | any JSON value (object, array, …) | `getValue()` |

**Boolean** flags are the classic on/off switch — use `enabled()`. **String**,
**number**, and **json** flags carry a value (a config knob, an A/B variant, a
limit, or a whole structured blob) — use `getValue()`. A `json` flag is handy
when you want to ship a small piece of configuration — a feature's settings, a
list of allowed values — as one flag.

```python
# boolean
if client.enabled("dark_mode", user={"user_id": "42"}):
    ...

# string / number
algo = client.get_value("search_algorithm", user={"user_id": "42"}, default="v1")
limit = client.get_value("max_results", user={"user_id": "42"}, default=10)

# json — returns a parsed object/array
cfg = client.get_value("checkout_config", user={"user_id": "42"}, default={})
```

## Default value vs enabled value

Non-boolean flags have **two** values, and which one a user gets is exactly the
question targeting answers:

- **`default_value`** — what a user gets when the flag is off, or when no rule and
  no rollout matches them. This is the safe fallback.
- **`enabled_value`** — what a *matched* user gets (a targeting rule hit, or they
  fell inside the rollout percentage).

So a `string` flag can serve `"v2"` to enterprise customers (`enabled_value`) and
`"v1"` to everyone else (`default_value`). For a **boolean** flag the on-value is
implicitly `true`, so there's only the one value to set.

> Always pass a sensible `default` to `get_value()` in code too. It's the value
> the SDK returns if the flag doesn't exist or no config has loaded yet — your
> last line of defence.

## Scope: one flag, many environments

A flag is defined once per project, but its **targeting** (on/off, rules, rollout
%) is set **per environment**. The same `new_checkout` flag can be on in
`development` and off in `production`. Non-boolean values are authored at the flag
level and can optionally be overridden per environment. See
[Environments & SDK keys](/docs/concepts/environments).

## Next

- [Targeting rules](/docs/concepts/targeting) — serve the enabled value to
  specific users.
- [Percentage rollouts](/docs/concepts/rollouts) — serve it to a fraction of
  users.
