BETA
Skip to content

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

ParameterTypeRequiredDescription
project_idstringYesProject to list flags for.
statusstringNoFilter by status: active, archived.
tags[]string[]NoTag 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 /flags

Required scope: flags:write

Request Body

FieldTypeRequiredDescription
project_idstringYesProject this flag belongs to.
keystringYesUnique flag key used in code (e.g., new-checkout-flow).
namestringYesHuman-readable display name.
descriptionstringNoDescription of what this flag controls.
flag_typestringNoOne of boolean, string, number, or json. Defaults to boolean.
default_valueanyNoDefault 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}.
enabledbooleanNoWhether the flag is enabled. Defaults to false.
statusstringNoFlag status: active or archived. Defaults to active.
tagsstring[]NoTags 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/:id

Required 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/:id

Required scope: flags:write

Request Body

FieldTypeRequiredDescription
keystringNoUnique flag key.
namestringNoHuman-readable display name.
descriptionstringNoDescription of what this flag controls.
flag_typestringNoOne of boolean, string, number, or json. Cannot be changed after the flag is created without re-creating downstream rules.
default_valueanyNoDefault value when no rules match. Bare values are wrapped to {"value": <bare>} automatically; see Create Flag for details.
enabledbooleanNoWhether the flag is enabled.
statusstringNoFlag status: active or archived.
tagsstring[]NoTags 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/:id

Required 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 Content

If 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/toggle

Required 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 FlagScheduleWorker Oban job runs every minute and toggles every flag whose scheduled_at is <= now — if the flag is currently enabled it becomes disabled, and vice versa. There is no separate "enable at" / "disable at" — scheduled_at is 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_at and writes a flag_scheduled_toggle entry to the audit log.
  • Calling POST /flags/:id/toggle manually clears any pending scheduled_at (Krafter.Flags.toggle_flag/2). Schedule a new value if you still want the future toggle.
  • Setting scheduled_at to null cancels 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/evaluate

Required scope: flags:read

Rate limit (strict)

This endpoint uses the strict bucket30 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

FieldTypeRequiredDescription
project_idstringYesProject to evaluate flags for.
environmentstringNoEnvironment to evaluate in. Defaults to "production".
contextobjectNoEvaluation 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 bucket30 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

ParameterTypeRequiredDescription
project_idstringYesProject to bootstrap flags for.
environmentstringNoEnvironment 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/track

Required scope: flags:write

Rate limit (strict)

This endpoint uses the strict bucket30 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

FieldTypeRequiredDescription
experiment_idstringYesID of the experiment to track a conversion for.
variant_keystringYesKey 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

ParameterTypeDefaultDescription
daysinteger7Number of days of history to include. Maximum: 90 — values above 90 are clamped server-side.
environmentstring(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=50

Required scope: flags:read

Query Parameters

ParameterTypeDefaultDescription
limitinteger50Maximum 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"
    }
  ]
}

Built by Krafter Studio