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

# Admin API

> Complete Admin API reference: environments, flags, targeting rules, rollouts, SDK key management, and audit logs. Auth via Supabase JWT.

The Admin API is the write-side surface for managing flags, environments, targeting rules, rollouts, and audit logs. It is authenticated with a Supabase JWT (obtained via `POST /auth/login`).

## Authentication

Admin endpoints require a Supabase JWT in the `Authorization` header:

```http theme={null}
Authorization: Bearer <supabase_jwt>
```

Obtain a JWT by logging in:

```http theme={null}
POST /auth/login
Content-Type: application/json

{
  "email": "admin@clubedge.live",
  "password": "your-password"
}
```

**Response `200 OK`:**

```json theme={null}
{
  "access_token": "<supabase_jwt>",
  "refresh_token": "<refresh_token>",
  "user": {
    "id": "user-uuid",
    "email": "admin@clubedge.live"
  }
}
```

Log out with:

```http theme={null}
POST /auth/logout
Authorization: Bearer <supabase_jwt>
```

Returns `204 No Content`.

## Environments

### Create an environment

```http theme={null}
POST /environments
Authorization: Bearer <jwt>
Content-Type: application/json

{
  "product": "workspace",
  "name": "production"
}
```

**Response `201 Created`:**

```json theme={null}
{
  "id": "env_1a2b3c4d",
  "product": "workspace",
  "name": "production",
  "sdkKey": "ffk_abc123...",
  "sdkKeyPrefix": "ffk_abc",
  "status": "active",
  "createdAt": "2026-08-01T04:00:00.000Z"
}
```

<Warning>
  The `sdkKey` is returned **only once** at creation time. Copy it into your secrets manager immediately — it cannot be retrieved again.
</Warning>

**Behind the scenes:**

* A new `environments` row is inserted with a SHA-256 hash of the SDK key (plaintext is never persisted).
* A `sdkKeyPrefix` is extracted for dashboard display.
* All existing active flags are auto-initialized for this environment with `isEnabled: false`.
* An `ENVIRONMENT_CREATED` audit log entry is written in the same transaction.

### List environments

```http theme={null}
GET /environments?page=1&limit=20&product=workspace&status=active
Authorization: Bearer <jwt>
```

**Response `200 OK`:**

```json theme={null}
{
  "items": [
    {
      "id": "env_1a2b3c4d",
      "product": "workspace",
      "name": "production",
      "sdkKeyPrefix": "ffk_abc",
      "status": "active",
      "createdAt": "2026-08-01T04:00:00.000Z"
    }
  ],
  "pagination": { "page": 1, "limit": 20, "total": 1 }
}
```

| Query param | Type                  | Default | Description                     |
| ----------- | --------------------- | ------- | ------------------------------- |
| `page`      | integer               | `1`     | Page number (min 1)             |
| `limit`     | integer               | `20`    | Items per page (min 1, max 100) |
| `product`   | string                | —       | Filter by product               |
| `status`    | `active` \| `revoked` | —       | Filter by lifecycle status      |

### Get an environment

```http theme={null}
GET /environments/{id}
Authorization: Bearer <jwt>
```

### Update an environment

Rename an environment (PATCH with `{ "name": "new-name" }`):

```http theme={null}
PATCH /environments/{id}
Authorization: Bearer <jwt>
Content-Type: application/json

{ "name": "production-v2" }
```

### Revoke an environment

Revokes an environment, making its SDK key inactive. Revoked environments cannot be evaluated by SDKs.

```http theme={null}
POST /environments/{id}/revoke
Authorization: Bearer <jwt>
```

**Response `200 OK`** — returns the revoked environment object with `status: "revoked"`.

### Rotate an SDK key

Rotates the SDK key for an environment. This revokes the old environment (status set to `revoked`) and creates a new active environment with the same product and name, migrating all flag configurations, targeting rules, and rollout configurations to the new environment. The new plaintext SDK key is returned only once.

```http theme={null}
POST /environments/{id}/rotate-key
Authorization: Bearer <jwt>
```

**Response `201 Created`:**

```json theme={null}
{
  "id": "env_new123",
  "product": "workspace",
  "name": "production",
  "sdkKey": "ffk_xyz789...",
  "sdkKeyPrefix": "ffk_xyz",
  "status": "active",
  "createdAt": "2026-08-05T12:00:00.000Z"
}
```

<Warning>
  Rotation invalidates the old key immediately. Update the key in your secrets manager and restart/re-deploy affected services before relying on the new key.
</Warning>

## Feature flags

### Create a flag

```http theme={null}
POST /flags
Authorization: Bearer <jwt>
Content-Type: application/json

{
  "key": "new_dashboard",
  "name": "New Dashboard",
  "description": "Enable the new dashboard UI",
  "type": "boolean"
}
```

| Field         | Type                                    | Required | Description                                   |
| ------------- | --------------------------------------- | -------- | --------------------------------------------- |
| `key`         | string                                  | Yes      | Human-readable identifier (unique, immutable) |
| `name`        | string                                  | Yes      | Display name                                  |
| `description` | string                                  | No       | Longer description                            |
| `type`        | `boolean` \| `string` \| `multivariate` | Yes      | Flag value type                               |

**Response `201 Created`:**

```json theme={null}
{
  "id": "flag_456def",
  "key": "new_dashboard",
  "name": "New Dashboard",
  "description": "Enable the new dashboard UI",
  "type": "boolean",
  "createdBy": "user_123",
  "createdAt": "2026-08-01T04:01:00.000Z",
  "archivedAt": null
}
```

<Note>
  Boolean flags are fully supported by the evaluation engine. String and multivariate types can be created, but the current evaluation engine only resolves boolean results — `isEnabled()` always returns `true` or `false`.
</Note>

### List flags

```http theme={null}
GET /flags?page=1&limit=20&archived=false
Authorization: Bearer <jwt>
```

| Query param | Type    | Default | Description                                                      |
| ----------- | ------- | ------- | ---------------------------------------------------------------- |
| `page`      | integer | `1`     | Page number                                                      |
| `limit`     | integer | `20`    | Items per page (max 100)                                         |
| `archived`  | boolean | —       | `true` for archived only, `false` for active only, omit for both |

### Get a flag

```http theme={null}
GET /flags/{id}
Authorization: Bearer <jwt>
```

### Update a flag

```http theme={null}
PATCH /flags/{id}
Authorization: Bearer <jwt>
Content-Type: application/json

{
  "name": "Updated Name",
  "description": "Updated description"
}
```

### Archive a flag

```http theme={null}
DELETE /flags/{id}
Authorization: Bearer <jwt>
```

Archived flags are retained for auditing. A flag cannot be archived while it is enabled in any environment. Returns `409 Conflict` if so.

## Flag environment configuration

### Get configuration

```http theme={null}
GET /flags/{flagId}/environments/{environmentId}
Authorization: Bearer <jwt>
```

**Response `200 OK`:**

```json theme={null}
{
  "flagId": "flag_456def",
  "environmentId": "env_1a2b3c4d",
  "isEnabled": true,
  "ttlOverrideSeconds": null
}
```

### Create or replace configuration

```http theme={null}
PUT /flags/{flagId}/environments/{environmentId}
Authorization: Bearer <jwt>
Content-Type: application/json

{
  "isEnabled": true,
  "ttlOverrideSeconds": 5
}
```

| Field                | Type            | Required | Description                                                                                                        |
| -------------------- | --------------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `isEnabled`          | boolean         | No       | Enable or disable the flag for this environment                                                                    |
| `ttlOverrideSeconds` | integer \| null | No       | Override the default 60s SDK cache TTL for this flag/env pair. Set to `null` to remove the override. Must be >= 1. |

**Behind the scenes:** Updates `feature_flag_environments`, writes a `FLAG_ENVIRONMENT_UPDATED` audit entry, and **invalidates the Redis cache** for this flag + environment.

## Targeting rules

### Create a rule

```http theme={null}
POST /flags/{flagId}/environments/{environmentId}/rules
Authorization: Bearer <jwt>
Content-Type: application/json

{
  "priority": 1,
  "logic": "and",
  "value": true,
  "conditions": [
    { "attribute": "country", "operator": "==", "valueType": "string", "valueString": "US" },
    { "attribute": "plan", "operator": "==", "valueType": "string", "valueString": "premium" }
  ]
}
```

| Field        | Type              | Required | Description                                                                                                 |
| ------------ | ----------------- | -------- | ----------------------------------------------------------------------------------------------------------- |
| `priority`   | integer           | Yes      | Evaluation order (lower = higher priority). Must be > 0. Must be unique within the same flag + environment. |
| `logic`      | `"and"` \| `"or"` | Yes      | How conditions combine                                                                                      |
| `value`      | boolean           | Yes      | What to return when this rule matches                                                                       |
| `conditions` | array             | Yes      | At least one condition                                                                                      |

Each condition uses a discriminated union — exactly one value field, determined by `valueType`:

| Field         | Type                                    | Required when `valueType` is              |
| ------------- | --------------------------------------- | ----------------------------------------- |
| `attribute`   | string                                  | always                                    |
| `operator`    | string                                  | always — `==`, `!=`, `>`, `<`, `>=`, `<=` |
| `valueType`   | `"string"` \| `"number"` \| `"boolean"` | always                                    |
| `valueString` | string                                  | `"string"`                                |
| `valueInt`    | integer                                 | `"number"`                                |
| `valueBool`   | boolean                                 | `"boolean"`                               |

<Warning>
  Only flat `and`/`or` logic is supported. Nested condition groups (e.g. `(A AND B) OR (C AND D)`) are not currently representable — use multiple prioritized rules instead.
</Warning>

### List rules

```http theme={null}
GET /flags/{flagId}/environments/{environmentId}/rules
Authorization: Bearer <jwt>
```

Returns rules ordered by `priority ASC`.

### Replace a rule

```http theme={null}
PUT /rules/{ruleId}
Authorization: Bearer <jwt>
Content-Type: application/json

{
  "priority": 2,
  "logic": "or",
  "value": false,
  "conditions": [
    { "attribute": "email", "operator": "==", "valueType": "string", "valueString": "internal@clubedge.live" }
  ]
}
```

### Delete a rule

```http theme={null}
DELETE /rules/{ruleId}
Authorization: Bearer <jwt>
```

## Rollouts

### Get rollout configuration

```http theme={null}
GET /flags/{flagId}/environments/{environmentId}/rollout
Authorization: Bearer <jwt>
```

### Create or update rollout

```http theme={null}
PUT /flags/{flagId}/environments/{environmentId}/rollout
Authorization: Bearer <jwt>
Content-Type: application/json

{
  "percentage": 25,
  "strategy": "per_user"
}
```

| Field        | Type                           | Required | Description                                                             |
| ------------ | ------------------------------ | -------- | ----------------------------------------------------------------------- |
| `percentage` | integer                        | Yes      | 0–100 (inclusive). Percentage of subjects that receive `enabled: true`. |
| `strategy`   | `"per_user"` \| `"per_tenant"` | Yes      | Bucketing strategy                                                      |

Returns `201 Created` on insert, `200 OK` on update.

### Delete rollout

```http theme={null}
DELETE /flags/{flagId}/environments/{environmentId}/rollout
Authorization: Bearer <jwt>
```

## Audit logs

```http theme={null}
GET /audit?flagKey=new_dashboard&limit=20&page=1
Authorization: Bearer <jwt>
```

| Query param   | Type      | Default | Description                |
| ------------- | --------- | ------- | -------------------------- |
| `page`        | integer   | `1`     | Page number                |
| `limit`       | integer   | `20`    | Items per page (max 100)   |
| `flagKey`     | string    | —       | Filter by flag key         |
| `environment` | string    | —       | Filter by environment name |
| `from`        | date-time | —       | Start of time range        |
| `to`          | date-time | —       | End of time range          |

**Response `200 OK`:**

```json theme={null}
{
  "items": [
    {
      "id": "log_001",
      "action": "FLAG_CREATED",
      "actorIdentity": "user_123",
      "occurredAt": "2026-08-01T04:01:00.000Z",
      "flagKey": "new_dashboard",
      "environment": null,
      "product": null,
      "oldValue": null,
      "newValue": { "key": "new_dashboard", "type": "boolean" }
    }
  ],
  "pagination": { "page": 1, "limit": 20, "total": 1 }
}
```

### Audit actions

| Action                     | When                                                             |
| -------------------------- | ---------------------------------------------------------------- |
| `FLAG_CREATED`             | A new flag was created                                           |
| `FLAG_UPDATED`             | Flag name or description was updated                             |
| `FLAG_ARCHIVED`            | Flag was archived                                                |
| `ENVIRONMENT_CREATED`      | A new environment was created                                    |
| `ENVIRONMENT_UPDATED`      | Environment name was updated                                     |
| `ENVIRONMENT_REVOKED`      | Environment was revoked                                          |
| `FLAG_ENVIRONMENT_UPDATED` | Flag enabled/disabled or TTL override changed for an environment |
| `RULE_CREATED`             | A targeting rule was created                                     |
| `RULE_UPDATED`             | A targeting rule was replaced                                    |
| `RULE_DELETED`             | A targeting rule was deleted                                     |
| `ROLLOUT_CONFIGURED`       | A rollout was created or updated                                 |
| `ROLLOUT_DELETED`          | A rollout was deleted                                            |
| `SDK_KEY_ROTATED`          | An environment's SDK key was rotated                             |

## Admin API endpoint summary

<Table>
  <TableHeader>
    <Row><Header>Method</Header><Header>Endpoint</Header><Header>Auth</Header><Header>Description</Header></Row>
  </TableHeader>

  <TableBody>
    <Row><Cell>POST</Cell><Cell>/auth/login</Cell><Cell>—</Cell><Cell>Get Supabase JWT</Cell></Row>
    <Row><Cell>POST</Cell><Cell>/auth/logout</Cell><Cell>JWT</Cell><Cell>Logout</Cell></Row>
    <Row><Cell>POST</Cell><Cell>/flags</Cell><Cell>JWT</Cell><Cell>Create flag</Cell></Row>
    <Row><Cell>GET</Cell><Cell>/flags</Cell><Cell>JWT</Cell><Cell>List flags (paginated)</Cell></Row>
    <Row><Cell>GET</Cell><Cell>/flags/:id</Cell><Cell>JWT</Cell><Cell>Get flag by ID</Cell></Row>
    <Row><Cell>PATCH</Cell><Cell>/flags/:id</Cell><Cell>JWT</Cell><Cell>Update flag (name, description)</Cell></Row>
    <Row><Cell>DELETE</Cell><Cell>/flags/:id</Cell><Cell>JWT</Cell><Cell>Archive flag</Cell></Row>
    <Row><Cell>PUT</Cell><Cell>/flags/:flagId/environments/:environmentId</Cell><Cell>JWT</Cell><Cell>Enable/disable flag in environment</Cell></Row>
    <Row><Cell>GET</Cell><Cell>/flags/:flagId/environments/:environmentId</Cell><Cell>JWT</Cell><Cell>Get flag-environment config</Cell></Row>
    <Row><Cell>POST</Cell><Cell>/flags/:flagId/environments/:environmentId/rules</Cell><Cell>JWT</Cell><Cell>Create targeting rule</Cell></Row>
    <Row><Cell>GET</Cell><Cell>/flags/:flagId/environments/:environmentId/rules</Cell><Cell>JWT</Cell><Cell>List targeting rules</Cell></Row>
    <Row><Cell>PUT</Cell><Cell>/rules/:ruleId</Cell><Cell>JWT</Cell><Cell>Replace targeting rule</Cell></Row>
    <Row><Cell>DELETE</Cell><Cell>/rules/:ruleId</Cell><Cell>JWT</Cell><Cell>Delete targeting rule</Cell></Row>
    <Row><Cell>PUT</Cell><Cell>/flags/:flagId/environments/:environmentId/rollout</Cell><Cell>JWT</Cell><Cell>Upsert rollout</Cell></Row>
    <Row><Cell>GET</Cell><Cell>/flags/:flagId/environments/:environmentId/rollout</Cell><Cell>JWT</Cell><Cell>Get rollout config</Cell></Row>
    <Row><Cell>DELETE</Cell><Cell>/flags/:flagId/environments/:environmentId/rollout</Cell><Cell>JWT</Cell><Cell>Delete rollout</Cell></Row>
    <Row><Cell>POST</Cell><Cell>/environments</Cell><Cell>JWT</Cell><Cell>Create environment</Cell></Row>
    <Row><Cell>GET</Cell><Cell>/environments</Cell><Cell>JWT</Cell><Cell>List environments</Cell></Row>
    <Row><Cell>GET</Cell><Cell>/environments/:id</Cell><Cell>JWT</Cell><Cell>Get environment</Cell></Row>
    <Row><Cell>PATCH</Cell><Cell>/environments/:id</Cell><Cell>JWT</Cell><Cell>Update environment name</Cell></Row>
    <Row><Cell>POST</Cell><Cell>/environments/:id/revoke</Cell><Cell>JWT</Cell><Cell>Revoke environment</Cell></Row>
    <Row><Cell>POST</Cell><Cell>/environments/:id/rotate-key</Cell><Cell>JWT</Cell><Cell>Rotate SDK key</Cell></Row>
    <Row><Cell>GET</Cell><Cell>/audit</Cell><Cell>JWT</Cell><Cell>List audit logs</Cell></Row>
    <Row><Cell>POST</Cell><Cell>/sdk/v1/evaluate</Cell><Cell>SDK Key</Cell><Cell>Evaluate single flag</Cell></Row>
    <Row><Cell>POST</Cell><Cell>/sdk/v1/evaluate/batch</Cell><Cell>SDK Key</Cell><Cell>Evaluate multiple flags</Cell></Row>
    <Row><Cell>GET</Cell><Cell>/sdk/v1/config</Cell><Cell>SDK Key</Cell><Cell>Fetch flag configs (ETag)</Cell></Row>
  </TableBody>
</Table>
