Appearance
Flags
Create, manage, and evaluate feature flags. Flags control feature rollouts, A/B tests, and application behavior across environments.
Base URL: https://app.krafter.dev/api/v1
ID format
All Flags resource IDs are PostgreSQL UUIDs (:binary_id). Examples in this document use the canonical 8-4-4-4-12 hex form (e.g. c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f).
List Flags
Retrieve all flags for a project, with optional filtering by status or tags.
GET /flags?project_id=...&status=...&tags[]=...&tags[]=...Required scope: flags:read
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
project_id | string | Yes | Project to list flags for. |
status | string | No | Filter by status: active, archived. |
tags[] | string[] | No | Tag filter using PostgreSQL array overlap (&&). Pass each tag as a repeated query parameter (?tags[]=foo&tags[]=bar). A flag matches when any of its tags overlap with the supplied set. Comma-separated single strings are not accepted by this endpoint and will fail at the database level. |
Example Request
bash
curl "https://app.krafter.dev/api/v1/flags?project_id=a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d&status=active&tags[]=checkout&tags[]=redesign" \
-H "Authorization: Bearer kr_live_abc123def456"Example Response
json
{
"data": [
{
"id": "c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
"key": "new-checkout-flow",
"name": "New Checkout Flow",
"description": "Redesigned checkout experience with single-page layout",
"flag_type": "boolean",
"default_value": {"value": false},
"enabled": true,
"status": "active",
"tags": ["checkout", "redesign"],
"scheduled_at": null,
"created_at": "2025-03-10T09:00:00Z",
"updated_at": "2025-03-14T16:30:00Z"
},
{
"id": "d4e5f6a7-8b9c-4d0e-9f1a-2b3c4d5e6f7a",
"key": "hero-banner-variant",
"name": "Hero Banner Variant",
"description": "A/B test for new hero banner design",
"flag_type": "string",
"default_value": {"value": "control"},
"enabled": true,
"status": "active",
"tags": ["experiment", "homepage"],
"scheduled_at": null,
"created_at": "2025-03-11T10:15:00Z",
"updated_at": "2025-03-11T10:15:00Z"
}
]
}Create Flag
Create a new feature flag.
POST /flagsRequired scope: flags:write
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
project_id | string | Yes | Project this flag belongs to. |
key | string | Yes | Unique flag key used in code (e.g., new-checkout-flow). |
name | string | Yes | Human-readable display name. |
description | string | No | Description of what this flag controls. |
flag_type | string | No | One of boolean, string, number, or json. Defaults to boolean. |
default_value | any | No | Default value when no rules match. Send a bare value (false, "control", 42, {"theme": "dark"}) — the API automatically wraps it as {"value": <bare>} before storage and returns the wrapped form on read. Pre-wrapped maps {"value": <bare>} are also accepted and stored verbatim. Defaults to {"value": false}. |
enabled | boolean | No | Whether the flag is enabled. Defaults to false. |
status | string | No | Flag status: active or archived. Defaults to active. |
tags | string[] | No | Tags for categorizing and filtering flags. |
Example Request
bash
curl -X POST https://app.krafter.dev/api/v1/flags \
-H "Authorization: Bearer kr_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"project_id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"key": "new-checkout-flow",
"name": "New Checkout Flow",
"description": "Redesigned checkout experience with single-page layout",
"flag_type": "boolean",
"default_value": false,
"enabled": true,
"tags": ["checkout", "redesign"]
}'Example Response
json
// 201 Created
{
"data": {
"id": "c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
"key": "new-checkout-flow",
"name": "New Checkout Flow",
"description": "Redesigned checkout experience with single-page layout",
"flag_type": "boolean",
"default_value": {"value": false},
"enabled": true,
"status": "active",
"tags": ["checkout", "redesign"],
"scheduled_at": null,
"created_at": "2025-03-10T09:00:00Z",
"updated_at": "2025-03-10T09:00:00Z"
}
}Get Flag
Retrieve details of a specific flag.
GET /flags/:idRequired scope: flags:read
Example Request
bash
curl https://app.krafter.dev/api/v1/flags/c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f \
-H "Authorization: Bearer kr_live_abc123def456"Example Response
json
{
"data": {
"id": "c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
"key": "new-checkout-flow",
"name": "New Checkout Flow",
"description": "Redesigned checkout experience with single-page layout",
"flag_type": "boolean",
"default_value": {"value": false},
"enabled": true,
"status": "active",
"tags": ["checkout", "redesign"],
"scheduled_at": null,
"created_at": "2025-03-10T09:00:00Z",
"updated_at": "2025-03-14T16:30:00Z"
}
}Update Flag
Update an existing flag. Only provided fields are changed.
PATCH /flags/:idRequired scope: flags:write
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
key | string | No | Unique flag key. |
name | string | No | Human-readable display name. |
description | string | No | Description of what this flag controls. |
flag_type | string | No | One of boolean, string, number, or json. Cannot be changed after the flag is created without re-creating downstream rules. |
default_value | any | No | Default value when no rules match. Bare values are wrapped to {"value": <bare>} automatically; see Create Flag for details. |
enabled | boolean | No | Whether the flag is enabled. |
status | string | No | Flag status: active or archived. |
tags | string[] | No | Tags for categorizing and filtering flags. |
Example Request
bash
curl -X PATCH https://app.krafter.dev/api/v1/flags/c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f \
-H "Authorization: Bearer kr_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"description": "Redesigned checkout with improved conversion flow",
"tags": ["checkout", "redesign", "q2"]
}'Example Response
json
{
"data": {
"id": "c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
"key": "new-checkout-flow",
"name": "New Checkout Flow",
"description": "Redesigned checkout with improved conversion flow",
"flag_type": "boolean",
"default_value": {"value": false},
"enabled": true,
"status": "active",
"tags": ["checkout", "redesign", "q2"],
"scheduled_at": null,
"created_at": "2025-03-10T09:00:00Z",
"updated_at": "2025-03-16T12:00:00Z"
}
}Delete Flag
Permanently delete a flag. The flag must be archived first (status: "archived").
DELETE /flags/:idRequired scope: flags:write
Example Request
bash
curl -X DELETE https://app.krafter.dev/api/v1/flags/c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f \
-H "Authorization: Bearer kr_live_abc123def456"Example Response
// 204 No ContentIf the flag is not archived:
json
// 422 Unprocessable Entity
{
"error": "Flag must be archived before it can be deleted"
}Toggle Flag
Toggle a flag's enabled state. If the flag is currently enabled, it will be disabled, and vice versa.
POST /flags/:id/toggleRequired scope: flags:write
Example Request
bash
curl -X POST https://app.krafter.dev/api/v1/flags/c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f/toggle \
-H "Authorization: Bearer kr_live_abc123def456"Example Response
json
{
"data": {
"id": "c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
"key": "new-checkout-flow",
"enabled": false,
"updated_at": "2025-03-16T14:00:00Z"
}
}Scheduled toggle
Schedule a future enable/disable by setting scheduled_at on the flag with Update Flag:
bash
curl -X PATCH https://app.krafter.dev/api/v1/flags/c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f \
-H "Authorization: Bearer kr_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{"scheduled_at": "2025-04-01T09:00:00Z"}'Behaviour:
- The
FlagScheduleWorkerOban job runs every minute and toggles every flag whosescheduled_atis<= now— if the flag is currently enabled it becomes disabled, and vice versa. There is no separate "enable at" / "disable at" —scheduled_atis a single inversion point. - Granularity is up to 60 seconds of drift after the scheduled time (worker tick interval).
- After toggling, the worker clears
scheduled_atand writes aflag_scheduled_toggleentry to the audit log. - Calling
POST /flags/:id/togglemanually clears any pendingscheduled_at(Krafter.Flags.toggle_flag/2). Schedule a new value if you still want the future toggle. - Setting
scheduled_attonullcancels a pending schedule without flipping the flag.
Evaluate Flags
Evaluate all flags for a given project and context. This is the primary endpoint used by SDKs to determine flag values for a specific user or session.
POST /flags/evaluateRequired scope: flags:read
Rate limit (strict)
This endpoint uses the strict bucket — 30 requests / 60 seconds per team, shared with bootstrap, track, push subscriber registration, and audit/scans/run. Cache evaluation results client-side rather than re-evaluating on every page view.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
project_id | string | Yes | Project to evaluate flags for. |
environment | string | No | Environment to evaluate in. Defaults to "production". |
context | object | No | Evaluation context (e.g., user attributes) used for targeting rules. |
Example Request
bash
curl -X POST https://app.krafter.dev/api/v1/flags/evaluate \
-H "Authorization: Bearer kr_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"project_id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"environment": "production",
"context": {
"user_id": "usr_123",
"plan": "pro",
"country": "US"
}
}'Example Response
json
{
"flags": {
"new-checkout-flow": {
"value": true,
"rule_id": "e5f6a7b8-9c0d-4e1f-9a2b-3c4d5e6f7a8b"
},
"hero-banner-variant": {
"value": "treatment",
"rule_id": "f6a7b8c9-0d1e-4f2a-9b3c-4d5e6f7a8b9c"
},
"dark-mode": {
"value": false,
"rule_id": null
}
}
}Bootstrap Flags
Retrieve all flag values for a project and environment without evaluation context. Used for server-side rendering or pre-loading flag defaults before client context is available.
GET /flags/bootstrap?project_id=...&environment=...Required scope: flags:read
Rate limit (strict)
This endpoint uses the strict bucket — 30 requests / 60 seconds per team, shared with evaluate, track, push subscriber registration, and audit/scans/run. Bootstrap once per server instance / page load, not per request.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
project_id | string | Yes | Project to bootstrap flags for. |
environment | string | No | Environment to bootstrap. Defaults to "production". |
Example Request
bash
curl "https://app.krafter.dev/api/v1/flags/bootstrap?project_id=a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d&environment=production" \
-H "Authorization: Bearer kr_live_abc123def456"Example Response
json
{
"flags": {
"new-checkout-flow": {
"value": true,
"rule_id": null
},
"hero-banner-variant": {
"value": "control",
"rule_id": null
},
"dark-mode": {
"value": false,
"rule_id": null
}
}
}Track Conversion
Track an experiment conversion event. Records that a user was exposed to a variant and completed the desired action.
POST /flags/trackRequired scope: flags:write
Rate limit (strict)
This endpoint uses the strict bucket — 30 requests / 60 seconds per team, shared with evaluate, bootstrap, push subscriber registration, and audit/scans/run. Batch tracking calls if your client emits more than 30 conversions per minute.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
experiment_id | string | Yes | ID of the experiment to track a conversion for. |
variant_key | string | Yes | Key of the variant the user was assigned to (e.g., "control" or "treatment"). |
Example Request
bash
curl -X POST https://app.krafter.dev/api/v1/flags/track \
-H "Authorization: Bearer kr_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"experiment_id": "3c4d5e6f-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
"variant_key": "treatment"
}'Example Response
json
{
"ok": true
}Get Flag Analytics
Retrieve evaluation analytics for a specific flag, including total evaluations, breakdown by value, and trends over time.
GET /flags/:id/analytics?days=...&environment=...Required scope: flags:read
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
days | integer | 7 | Number of days of history to include. Maximum: 90 — values above 90 are clamped server-side. |
environment | string | (all) | Restrict the rollup to a single environment (e.g., production). When omitted, all environments are aggregated. |
Example Request
bash
curl "https://app.krafter.dev/api/v1/flags/c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f/analytics?days=30&environment=production" \
-H "Authorization: Bearer kr_live_abc123def456"Example Response
json
{
"data": {
"flag_id": "c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
"total_evaluations": 15420,
"by_value": {
"true": 12336,
"false": 3084
},
"by_environment": {
"production": 14200,
"staging": 1220
},
"last_24h": {
"evaluations": 2150,
"unique_contexts": 890
}
}
}Get Flag Audit Log
Retrieve the audit log for a specific flag, showing all changes made over time.
GET /flags/:id/audit-log?limit=50Required scope: flags:read
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Maximum number of entries to return. Maximum: 200 — values above 200 are clamped server-side. |
Example Request
bash
curl "https://app.krafter.dev/api/v1/flags/c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f/audit-log?limit=10" \
-H "Authorization: Bearer kr_live_abc123def456"Example Response
json
{
"data": [
{
"id": "6f7a8b9c-0d1e-4f2a-9b3c-4d5e6f7a8b9c",
"action": "flag.toggled",
"changes": {
"enabled": {
"from": true,
"to": false
}
},
"metadata": {
"ip": "203.0.113.42",
"user_agent": "Mozilla/5.0"
},
"flag_id": "c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
"user_id": "8b9c0d1e-2f3a-4b5c-9d6e-7f8a9b0c1d2e",
"created_at": "2025-03-16T14:00:00Z"
},
{
"id": "7a8b9c0d-1e2f-4a3b-9c4d-5e6f7a8b9c0d",
"action": "flag.updated",
"changes": {
"description": {
"from": "Redesigned checkout experience with single-page layout",
"to": "Redesigned checkout with improved conversion flow"
}
},
"metadata": {
"ip": "203.0.113.42",
"user_agent": "Mozilla/5.0"
},
"flag_id": "c3d4e5f6-7a8b-4c9d-8e0f-1a2b3c4d5e6f",
"user_id": "8b9c0d1e-2f3a-4b5c-9d6e-7f8a9b0c1d2e",
"created_at": "2025-03-16T12:00:00Z"
}
]
}