> ## Documentation Index
> Fetch the complete documentation index at: https://docs.swisstools.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Feature Flags API Reference

> Manage global flags, flag groups, group records, and per-record flag overrides. Resolve flags at runtime via the SDK endpoint with an API key.

Feature flags in Swisstools are organized at two levels. **Global flags** apply project-wide with an on/off toggle and an optional rollout percentage. **Flag groups** let you define a reusable set of flags (group flags) and then apply them to individual records — users, organizations, or any entity you identify — with per-record overrides. The SDK endpoint resolves all flag values for a record using an API key, so your application can fetch flag state at runtime without a session cookie.

Every endpoint below is project-scoped and accepts **either** a dashboard session cookie **or** a project API key (`Authorization: Bearer fk_…` or the `x-api-key` header) — API keys are project-admin equivalent. The SDK endpoint (`GET /api/flags/:recordId`) is the one route that requires an API key. See [Authentication](/api-reference/authentication) for details.

Project, flag, group, record, and environment IDs in these paths are **public reference IDs** (KSUIDs, \~27 chars) shown in the dashboard — the internal database UUID is not accepted in URLs.

Flags and records resolve against an **environment**. Pass an optional `?env=` query parameter (the environment's `key` such as `production`, or its display `name`, case-insensitive) on read and check endpoints; when omitted, the project's default environment is used.

***

## Global Flags

### List Flags

`GET /api/projects/:projectId/flags`

Returns all global feature flags for the project, scoped to an environment.

**Path Parameters**

<ParamField path="projectId" type="string" required>
  The project's public reference ID (KSUID), shown in **Project settings → General**.
</ParamField>

<ParamField query="env" type="string">
  Optional environment selector (the environment's `key` or `name`). Defaults to the project's default environment; an unknown value returns `[]`.
</ParamField>

**Request**

```bash theme={null}
curl https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/flags \
  -b cookies.txt
```

**Response**

```json theme={null}
[
  {
    "id": "2a9bX1c0d3e4f5g6h7i8j9k0",
    "projectId": "3DS7IwHB2HHfSP61DzDHVTXt7KK",
    "name": "Dark Mode",
    "key": "dark-mode",
    "enabled": true,
    "rollout": 100,
    "environment": { "id": "1Rkx7yLBcnX2tGZ4VqHfJp9wYsT", "name": "Production", "key": "production" },
    "createdAt": "2024-03-01T10:00:00.000Z",
    "updatedAt": "2024-03-01T10:00:00.000Z"
  }
]
```

### Create Flag

`POST /api/projects/:projectId/flags`

Creates a new global feature flag.

**Path Parameters**

<ParamField path="projectId" type="string" required>
  The project's public reference ID (KSUID), shown in **Project settings → General**.
</ParamField>

**Request Body**

<ParamField body="name" type="string" required>
  Display name for the flag.
</ParamField>

<ParamField body="id" type="string">
  Optional user-supplied public reference ID (`^[A-Za-z0-9_-]{1,128}$`). Stored as-is and must be unique across the project's flags. When omitted, a KSUID is generated.
</ParamField>

<ParamField body="key" type="string">
  A unique, URL-friendly identifier for the flag within this project (e.g., `dark-mode`). Defaults to a slug of `name` when omitted. Must be unique per project and environment.
</ParamField>

<ParamField body="enabled" type="boolean">
  Whether the flag is active. Defaults to `false`.
</ParamField>

<ParamField body="rollout" type="integer">
  Rollout percentage from `0` to `100`. Defaults to `100`.
</ParamField>

<ParamField body="environmentKey" type="string">
  Environment the flag is created in (accepts an environment `key` or `name`). Defaults to the project's default environment.
</ParamField>

**Request**

```bash theme={null}
curl -X POST https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/flags \
  -b cookies.txt \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Dark Mode",
    "key": "dark-mode",
    "enabled": true,
    "rollout": 100
  }'
```

**Response** — `201 Created`

```json theme={null}
{
  "id": "2a9bX1c0d3e4f5g6h7i8j9k0",
  "projectId": "3DS7IwHB2HHfSP61DzDHVTXt7KK",
  "name": "Dark Mode",
  "key": "dark-mode",
  "enabled": true,
  "rollout": 100,
  "environment": { "id": "1Rkx7yLBcnX2tGZ4VqHfJp9wYsT", "name": "Production", "key": "production" },
  "createdAt": "2024-03-01T10:00:00.000Z",
  "updatedAt": "2024-03-01T10:00:00.000Z"
}
```

### Get Flag

`GET /api/projects/:projectId/flags/:flagId`

Returns a single global flag by its ID.

**Path Parameters**

<ParamField path="projectId" type="string" required>
  The project's public reference ID (KSUID), shown in **Project settings → General**.
</ParamField>

<ParamField path="flagId" type="string" required>
  The flag's public reference ID — a KSUID, or the value you supplied as `id` when creating the flag.
</ParamField>

**Request**

```bash theme={null}
curl https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/flags/2a9bX1c0d3e4f5g6h7i8j9k0 \
  -b cookies.txt
```

**Response**

```json theme={null}
{
  "id": "2a9bX1c0d3e4f5g6h7i8j9k0",
  "projectId": "3DS7IwHB2HHfSP61DzDHVTXt7KK",
  "name": "Dark Mode",
  "key": "dark-mode",
  "enabled": true,
  "rollout": 100,
  "environment": { "id": "1Rkx7yLBcnX2tGZ4VqHfJp9wYsT", "name": "Production", "key": "production" },
  "createdAt": "2024-03-01T10:00:00.000Z",
  "updatedAt": "2024-03-01T10:00:00.000Z"
}
```

### Update Flag

`PUT /api/projects/:projectId/flags/:flagId`

Updates a global flag's name, enabled state, or rollout percentage.

**Path Parameters**

<ParamField path="projectId" type="string" required>
  The project's public reference ID (KSUID), shown in **Project settings → General**.
</ParamField>

<ParamField path="flagId" type="string" required>
  The flag's public reference ID — a KSUID, or the value you supplied as `id` when creating the flag.
</ParamField>

**Request Body**

<ParamField body="name" type="string">
  Updated display name.
</ParamField>

<ParamField body="enabled" type="boolean">
  Updated enabled state.
</ParamField>

<ParamField body="rollout" type="integer">
  Updated rollout percentage.
</ParamField>

**Request**

```bash theme={null}
curl -X PUT https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/flags/2a9bX1c0d3e4f5g6h7i8j9k0 \
  -b cookies.txt \
  -H "Content-Type: application/json" \
  -d '{"enabled": false, "rollout": 0}'
```

**Response**

```json theme={null}
{
  "id": "2a9bX1c0d3e4f5g6h7i8j9k0",
  "projectId": "3DS7IwHB2HHfSP61DzDHVTXt7KK",
  "name": "Dark Mode",
  "key": "dark-mode",
  "enabled": false,
  "rollout": 0,
  "environment": { "id": "1Rkx7yLBcnX2tGZ4VqHfJp9wYsT", "name": "Production", "key": "production" },
  "createdAt": "2024-03-01T10:00:00.000Z",
  "updatedAt": "2024-04-01T08:00:00.000Z"
}
```

### Delete Flag

`DELETE /api/projects/:projectId/flags/:flagId`

Permanently deletes a global flag.

**Request**

```bash theme={null}
curl -X DELETE https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/flags/2a9bX1c0d3e4f5g6h7i8j9k0 \
  -b cookies.txt
```

**Response**

```json theme={null}
{"success": true}
```

### Check Flag

`GET /api/projects/:projectId/flags/check/:flagKey`

Checks whether a global flag is currently enabled by its `key`. Useful for server-side flag resolution without going through the SDK.

**Path Parameters**

<ParamField path="projectId" type="string" required>
  The project's public reference ID (KSUID), shown in **Project settings → General**.
</ParamField>

<ParamField path="flagKey" type="string" required>
  The `key` value of the flag (e.g., `dark-mode`).
</ParamField>

<ParamField query="env" type="string">
  Optional environment selector (the environment's `key` or `name`, case-insensitive). Defaults to the project's default environment; an unknown value returns `404`. Results are cached for 60 seconds in Redis per `(project, flagKey, env)`.
</ParamField>

**Request**

```bash theme={null}
curl https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/flags/check/dark-mode \
  -b cookies.txt
```

**Response**

```json theme={null}
{
  "enabled": true,
  "rollout": 75,
  "source": "global",
  "environment": { "id": "1Rkx7yLBcnX2tGZ4VqHfJp9wYsT", "name": "Production", "key": "production" }
}
```

`enabled` already reflects the rollout roll. `source` is `"cache"` when served from the Redis cache or `"global"` when freshly computed.

***

## Flag Groups

Groups are reusable collections of flags that you attach to records (users, orgs, etc.). Each group has its own set of group flags with default values; records in the group can override any flag's value.

### List Groups

`GET /api/projects/:projectId/groups`

**Request**

```bash theme={null}
curl https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/groups \
  -b cookies.txt
```

**Response**

```json theme={null}
[
  {
    "id": "1Rz7Y9LpH8mC3nQ4VkRwBpTfYsT",
    "projectId": "3DS7IwHB2HHfSP61DzDHVTXt7KK",
    "name": "Beta Users",
    "key": "beta-users",
    "prefix": "beta",
    "description": null,
    "color": "#8C7AB0",
    "createdAt": "2024-03-05T09:00:00.000Z",
    "updatedAt": "2024-03-05T09:00:00.000Z"
  }
]
```

### Create Group

`POST /api/projects/:projectId/groups`

**Request Body**

<ParamField body="name" type="string" required>
  Display name for the group.
</ParamField>

<ParamField body="key" type="string">
  Unique identifier for the group within this project. Defaults to a slug of `name` when omitted.
</ParamField>

<ParamField body="prefix" type="string">
  A 2–5 character lowercase alphanumeric prefix used to namespace auto-generated record IDs in this group (e.g., `beta`). Must be unique per project. Auto-derived from `name` when omitted.
</ParamField>

<ParamField body="description" type="string">
  Optional description for the group.
</ParamField>

<ParamField body="color" type="string">
  Optional hex color used as the group's label in the dashboard (e.g., `#8C7AB0`).
</ParamField>

**Request**

```bash theme={null}
curl -X POST https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/groups \
  -b cookies.txt \
  -H "Content-Type: application/json" \
  -d '{"name": "Beta Users", "key": "beta-users", "prefix": "beta"}'
```

**Response** — `201 Created`

```json theme={null}
{
  "id": "1Rz7Y9LpH8mC3nQ4VkRwBpTfYsT",
  "projectId": "3DS7IwHB2HHfSP61DzDHVTXt7KK",
  "name": "Beta Users",
  "key": "beta-users",
  "prefix": "beta",
  "description": null,
  "color": "#8C7AB0",
  "createdAt": "2024-03-05T09:00:00.000Z",
  "updatedAt": "2024-03-05T09:00:00.000Z"
}
```

### Get Group

`GET /api/projects/:projectId/groups/:groupId`

**Request**

```bash theme={null}
curl https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/groups/1Rz7Y9LpH8mC3nQ4VkRwBpTfYsT \
  -b cookies.txt
```

**Response**

```json theme={null}
{
  "id": "1Rz7Y9LpH8mC3nQ4VkRwBpTfYsT",
  "projectId": "3DS7IwHB2HHfSP61DzDHVTXt7KK",
  "name": "Beta Users",
  "key": "beta-users",
  "prefix": "beta",
  "description": null,
  "color": "#8C7AB0",
  "createdAt": "2024-03-05T09:00:00.000Z",
  "updatedAt": "2024-03-05T09:00:00.000Z"
}
```

### Update Group

`PUT /api/projects/:projectId/groups/:groupId`

**Request Body**

<ParamField body="name" type="string">
  Updated display name.
</ParamField>

<ParamField body="key" type="string">
  Updated key.
</ParamField>

<ParamField body="prefix" type="string">
  Updated prefix.
</ParamField>

**Request**

```bash theme={null}
curl -X PUT https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/groups/1Rz7Y9LpH8mC3nQ4VkRwBpTfYsT \
  -b cookies.txt \
  -H "Content-Type: application/json" \
  -d '{"name": "Early Access Users"}'
```

**Response**

```json theme={null}
{
  "id": "1Rz7Y9LpH8mC3nQ4VkRwBpTfYsT",
  "projectId": "3DS7IwHB2HHfSP61DzDHVTXt7KK",
  "name": "Early Access Users",
  "key": "beta-users",
  "prefix": "beta",
  "description": null,
  "color": "#8C7AB0",
  "createdAt": "2024-03-05T09:00:00.000Z",
  "updatedAt": "2024-04-10T14:00:00.000Z"
}
```

### Delete Group

`DELETE /api/projects/:projectId/groups/:groupId`

**Request**

```bash theme={null}
curl -X DELETE https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/groups/1Rz7Y9LpH8mC3nQ4VkRwBpTfYsT \
  -b cookies.txt
```

**Response**

```json theme={null}
{"success": true}
```

***

## Group Flags

Group flags are the individual flags within a group. Each has a `defaultEnabled` and `defaultPercentage` that applies to all records unless overridden.

### List Group Flags

`GET /api/projects/:projectId/groups/:groupId/flags`

**Request**

```bash theme={null}
curl https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/groups/1Rz7Y9LpH8mC3nQ4VkRwBpTfYsT/flags \
  -b cookies.txt
```

**Response**

```json theme={null}
[
  {
    "id": "4kPq2Lm8nR3sT5vW7xY9zA1bC3d",
    "groupId": "1Rz7Y9LpH8mC3nQ4VkRwBpTfYsT",
    "name": "New Dashboard",
    "key": "new-dashboard",
    "defaultEnabled": true,
    "defaultPercentage": 100,
    "createdAt": "2024-03-05T09:30:00.000Z",
    "updatedAt": "2024-03-05T09:30:00.000Z"
  }
]
```

### Create Group Flag

`POST /api/projects/:projectId/groups/:groupId/flags`

**Request Body**

<ParamField body="name" type="string" required>
  Display name for the flag.
</ParamField>

<ParamField body="key" type="string" required>
  Unique identifier within the group.
</ParamField>

<ParamField body="defaultEnabled" type="boolean">
  Default enabled state for records that have no override. Defaults to `false`.
</ParamField>

<ParamField body="defaultPercentage" type="integer">
  Default rollout percentage. Defaults to `100`.
</ParamField>

**Request**

```bash theme={null}
curl -X POST https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/groups/1Rz7Y9LpH8mC3nQ4VkRwBpTfYsT/flags \
  -b cookies.txt \
  -H "Content-Type: application/json" \
  -d '{
    "name": "New Dashboard",
    "key": "new-dashboard",
    "defaultEnabled": true,
    "defaultPercentage": 100
  }'
```

**Response** — `201 Created`

```json theme={null}
{
  "id": "4kPq2Lm8nR3sT5vW7xY9zA1bC3d",
  "groupId": "1Rz7Y9LpH8mC3nQ4VkRwBpTfYsT",
  "name": "New Dashboard",
  "key": "new-dashboard",
  "defaultEnabled": true,
  "defaultPercentage": 100,
  "createdAt": "2024-03-05T09:30:00.000Z",
  "updatedAt": "2024-03-05T09:30:00.000Z"
}
```

***

## Group Records

Records represent the entities (users, organizations, devices, etc.) that belong to a group. Each record's `id` is either a stable identifier you supply from your own system, or — when you omit it — an auto-generated `{groupPrefix}-{ksuid}` value (e.g., `beta-2a9bX1c0d3e4f5g6h7i8j9k0`).

### List Records

`GET /api/projects/:projectId/groups/:groupId/records`

**Request**

```bash theme={null}
curl https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/groups/1Rz7Y9LpH8mC3nQ4VkRwBpTfYsT/records \
  -b cookies.txt
```

**Response**

```json theme={null}
[
  {
    "id": "user_alice_123",
    "group": { "id": "1Rz7Y9LpH8mC3nQ4VkRwBpTfYsT", "name": "Beta Users" },
    "name": "Alice",
    "metadata": {"plan": "pro", "region": "us-east"},
    "createdAt": "2024-03-10T08:00:00.000Z",
    "updatedAt": "2024-03-10T08:00:00.000Z"
  }
]
```

### Create Record

`POST /api/projects/:projectId/groups/:groupId/records`

Adds a record (entity) to the group.

**Request Body**

<ParamField body="id" type="string">
  Optional stable identifier from your own system (`^[A-Za-z0-9_-]{1,128}$`), stored verbatim and unique across all records. When omitted, the server generates `{groupPrefix}-{ksuid}`. Supplying your own entity ID lets SDK callers resolve flags via `GET /api/flags/{yourId}` directly.
</ParamField>

<ParamField body="name" type="string">
  Optional human-readable label for this record, shown in the dashboard.
</ParamField>

<ParamField body="metadata" type="object">
  Optional JSON object with arbitrary key-value pairs (e.g., plan, region, cohort).
</ParamField>

**Request** — user-supplied ID

```bash theme={null}
curl -X POST https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/groups/1Rz7Y9LpH8mC3nQ4VkRwBpTfYsT/records \
  -b cookies.txt \
  -H "Content-Type: application/json" \
  -d '{
    "id": "user_alice_123",
    "name": "Alice",
    "metadata": {"plan": "pro", "region": "us-east"}
  }'
```

To let the server generate the ID instead, omit `id` (e.g. `-d '{"name": "Alice"}'`); the response `id` will be `{groupPrefix}-{ksuid}`.

**Response** — `201 Created`

```json theme={null}
{
  "id": "user_alice_123",
  "group": { "id": "1Rz7Y9LpH8mC3nQ4VkRwBpTfYsT", "name": "Beta Users" },
  "name": "Alice",
  "metadata": {"plan": "pro", "region": "us-east"},
  "createdAt": "2024-03-10T08:00:00.000Z",
  "updatedAt": "2024-03-10T08:00:00.000Z"
}
```

***

## Environments

Flags and overrides are scoped to environments (e.g. `production`, `staging`). Reference an environment by its `key` or `name` via the `?env=` query parameter on read/check endpoints; omitting it uses the project's default environment.

### List Environments

`GET /api/projects/:projectId/environments`

```bash theme={null}
curl https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/environments \
  -b cookies.txt
```

**Response**

```json theme={null}
[
  { "id": "1Rkx7yLBcnX2tGZ4VqHfJp9wYsT", "name": "Production", "key": "production", "isDefault": true }
]
```

### Create Environment

`POST /api/projects/:projectId/environments`

**Request Body**

<ParamField body="name" type="string" required>
  Display name for the environment (e.g., `Staging`).
</ParamField>

<ParamField body="key" type="string">
  URL-friendly identifier (e.g., `staging`). Defaults to a slug of `name`.
</ParamField>

<ParamField body="isDefault" type="boolean">
  Whether this becomes the project's default environment. Defaults to `false`.
</ParamField>

```bash theme={null}
curl -X POST https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/environments \
  -b cookies.txt \
  -H "Content-Type: application/json" \
  -d '{"name": "Staging", "key": "staging"}'
```

***

## Per-Record Overrides

Set or clear an individual record's value for a group flag in a given environment:

`PUT /api/projects/:projectId/groups/:groupId/records/:recordId/values/:groupFlagId`

```bash theme={null}
curl -X PUT https://swisstools.dev/api/projects/3DS7IwHB2HHfSP61DzDHVTXt7KK/groups/1Rz7Y9LpH8mC3nQ4VkRwBpTfYsT/records/user_alice_123/values/4kPq2Lm8nR3sT5vW7xY9zA1bC3d \
  -b cookies.txt \
  -H "Content-Type: application/json" \
  -d '{"enabled": true, "rollout": 100, "environmentKey": "production"}'
```

To read the raw overrides currently set for a record (override-vs-default state, before the rollout roll), use `GET /api/projects/:projectId/records/:recordId/values`.

***

## SDK Endpoint

`GET /api/flags/:recordId`

Resolves all feature flag values for a given record. This endpoint uses **API key bearer auth** rather than a session cookie, so you can call it from your backend or edge functions at runtime.

The response maps each group flag `key` to its resolved value for the specified record and environment, taking per-record overrides into account. `enabled` is the **final** decision — the server applies the rollout dice-roll, so a flag that is on but outside the rollout returns `enabled: false`.

**Path Parameters**

<ParamField path="recordId" type="string" required>
  The ID of the record whose flags you want to resolve — the auto-generated `{groupPrefix}-{ksuid}` form or the value you supplied as `id`. Must belong to the same project as the API key.
</ParamField>

<ParamField query="env" type="string">
  Optional environment selector (the environment's `key` or `name`). Defaults to the project's default environment. If it doesn't match any environment, `environment` is `null` and `flags` is `{}`.
</ParamField>

**Request**

```bash theme={null}
curl https://swisstools.dev/api/flags/user_alice_123 \
  -H "Authorization: Bearer fk_2a9bX1c0d3e4f5g6h7i8j9k0"
```

**Response**

```json theme={null}
{
  "recordId": "user_alice_123",
  "projectId": "3DS7IwHB2HHfSP61DzDHVTXt7KK",
  "environment": { "id": "1Rkx7yLBcnX2tGZ4VqHfJp9wYsT", "name": "Production", "key": "production" },
  "flags": {
    "new-dashboard": {"enabled": true, "rollout": 100, "source": "record"},
    "beta-export":   {"enabled": false, "rollout": 0, "source": "group-default"}
  }
}
```

Each flag's `source` is `"record"` when the value comes from a per-record override, or `"group-default"` when it falls back to the group flag's `defaultEnabled` / `defaultPercentage`.
