Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Cloudflare platform skill covering Workers, D1, R2, KV, AI, Durable Objects, and security.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/flagship/api.md
1# Flagship API Reference23## Binding API (Workers)45The binding is available as `env.FLAGS` (type `Flagship` from `@cloudflare/workers-types`).67### Evaluation Methods89All methods are async, never throw, and return the `defaultValue` on errors.1011| Method | Signature | Returns |12|--------|-----------|---------|13| `get` | `get(flagKey, defaultValue?, context?)` | `Promise<unknown>` |14| `getBooleanValue` | `getBooleanValue(flagKey, defaultValue, context?)` | `Promise<boolean>` |15| `getStringValue` | `getStringValue(flagKey, defaultValue, context?)` | `Promise<string>` |16| `getNumberValue` | `getNumberValue(flagKey, defaultValue, context?)` | `Promise<number>` |17| `getObjectValue` | `getObjectValue<T>(flagKey, defaultValue, context?)` | `Promise<T>` |18| `getBooleanDetails` | `getBooleanDetails(flagKey, defaultValue, context?)` | `Promise<FlagshipEvaluationDetails<boolean>>` |19| `getStringDetails` | `getStringDetails(flagKey, defaultValue, context?)` | `Promise<FlagshipEvaluationDetails<string>>` |20| `getNumberDetails` | `getNumberDetails(flagKey, defaultValue, context?)` | `Promise<FlagshipEvaluationDetails<number>>` |21| `getObjectDetails` | `getObjectDetails<T>(flagKey, defaultValue, context?)` | `Promise<FlagshipEvaluationDetails<T>>` |2223### Parameters (shared across all methods)2425| Parameter | Type | Required | Description |26|-----------|------|----------|-------------|27| `flagKey` | `string` | Yes | Flag key to evaluate |28| `defaultValue` | varies | Yes (except `get`) | Fallback if evaluation fails or flag not found |29| `context` | `FlagshipEvaluationContext` | No | Attributes for targeting rules (`{ userId: "user-42", country: "US" }`) |3031### Types3233```typescript34type FlagshipEvaluationContext = Record<string, string | number | boolean>;3536interface FlagshipEvaluationDetails<T> {37flagKey: string;38value: T;39variant?: string; // name of the matched variation40reason?: string; // "TARGETING_MATCH" | "DEFAULT" | "DISABLED" | "SPLIT"41errorCode?: string; // "TYPE_MISMATCH" | "GENERAL"42errorMessage?: string;43}44```4546### Example4748```typescript49export default {50async fetch(request: Request, env: Env): Promise<Response> {51const enabled = await env.FLAGS.getBooleanValue("new-feature", false, {52userId: "user-42",53});54return new Response(enabled ? "Feature on" : "Feature off");55},56};57```5859---6061## OpenFeature SDK6263Package: `@cloudflare/flagship`6465### Server Provider (`FlagshipServerProvider`)6667For Workers, Node.js, and server-side JavaScript.6869**With binding (recommended inside Workers):**7071```typescript72import { OpenFeature } from "@openfeature/server-sdk";73import { FlagshipServerProvider } from "@cloudflare/flagship";7475await OpenFeature.setProviderAndWait(76new FlagshipServerProvider({ binding: env.FLAGS }),77);78const client = OpenFeature.getClient();79const enabled = await client.getBooleanValue("new-checkout", false, {80targetingKey: "user-42",81});82```8384**With app ID (Node.js / non-Worker runtimes):**8586```typescript87import { OpenFeature } from "@openfeature/server-sdk";88import { FlagshipServerProvider } from "@cloudflare/flagship";8990await OpenFeature.setProviderAndWait(91new FlagshipServerProvider({92appId: "<APP_ID>",93accountId: "<ACCOUNT_ID>",94authToken: "<API_TOKEN>",95}),96);97const client = OpenFeature.getClient();98const enabled = await client.getBooleanValue("new-checkout", false, {99targetingKey: "user-42",100});101```102103### Client Provider (`FlagshipClientProvider`)104105For browser applications. Pre-fetches flags on init, evaluates synchronously.106107```typescript108import { OpenFeature } from "@openfeature/web-sdk";109import { FlagshipClientProvider } from "@cloudflare/flagship";110111await OpenFeature.setProviderAndWait(112new FlagshipClientProvider({113appId: "<APP_ID>",114accountId: "<ACCOUNT_ID>",115authToken: "<API_TOKEN>",116prefetchFlags: ["promo-banner", "dark-mode"],117}),118);119await OpenFeature.setContext({ targetingKey: "user-42", plan: "enterprise" });120const client = OpenFeature.getClient();121122// Synchronous — no await needed123const showBanner = client.getBooleanValue("promo-banner", false);124```125126**Important:** Only flags listed in `prefetchFlags` are available. Unlisted flags return `FLAG_NOT_FOUND`.127128### SDK Hooks129130```typescript131import { LoggingHook, TelemetryHook } from "@cloudflare/flagship";132OpenFeature.addHooks(new LoggingHook(), new TelemetryHook());133```134135---136137## REST API (Flag Management)138139Source of truth: [Cloudflare Flagship API reference](https://developers.cloudflare.com/api/resources/flagship/). Use it to verify REST paths, envelopes, response fields, and permission wording before relying on examples here.140141### FIRST: Check Prerequisites142143Before making any REST API calls (create, read, update, delete, toggle flags), verify these environment variables are set:144145| Variable | Purpose | How to get |146|----------|---------|------------|147| `CLOUDFLARE_ACCOUNT_ID` | Account identifier | Dashboard URL or `wrangler whoami` |148| `CLOUDFLARE_API_TOKEN` | Bearer token for API auth | [Create API token](https://dash.cloudflare.com/profile/api-tokens) with Flagship permissions |149| `FLAGSHIP_APP_ID` | Target app UUID | Dashboard under **Compute > Flagship**, or `GET /apps` endpoint |150151Check with:152153```bash154echo "CLOUDFLARE_ACCOUNT_ID=${CLOUDFLARE_ACCOUNT_ID:-(not set)}"155echo "CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN:-(not set)}"156echo "FLAGSHIP_APP_ID=${FLAGSHIP_APP_ID:-(not set)}"157```158159**If any are missing, ask the user to provide them before proceeding.**160161### Base URL and Auth162163Base URL: `https://api.cloudflare.com/client/v4/accounts/{account_id}/flagship`164165Authentication: `Authorization: Bearer <API_TOKEN>`166167Management endpoints use the Cloudflare v4 envelope. On success, the payload is under `result`; errors are an array under `errors`.168169```jsonc170// Success171{ "success": true, "result": <T>, "errors": [], "messages": [] }172173// Paginated success174{175"success": true,176"result": [<T>],177"result_info": { "count": 50, "cursor": "next-cursor-or-null" },178"errors": [],179"messages": []180}181182// Error183{ "success": false, "result": null, "errors": [{ "message": "message" }], "messages": [] }184```185186### App Endpoints187188| Method | Path | Description |189|--------|------|-------------|190| `GET` | `/apps` | List all apps |191| `GET` | `/apps/{app_id}` | Get app |192| `POST` | `/apps` | Create app (`{ "name": "my-app" }`) |193| `PUT` | `/apps/{app_id}` | Update app (`{ "name": "new-name" }`) |194| `DELETE` | `/apps/{app_id}` | Delete app |195196App name constraints: alphanumeric + hyphens + underscores, 1-64 chars.197198### Flag Endpoints199200| Method | Path | Description |201|--------|------|-------------|202| `GET` | `/apps/{app_id}/flags?limit=50&cursor=<cursor>` | List flags (paginated) |203| `GET` | `/apps/{app_id}/flags/{flag_key}` | Get flag |204| `POST` | `/apps/{app_id}/flags` | Create flag |205| `PUT` | `/apps/{app_id}/flags/{flag_key}` | Update flag (full replace) |206| `DELETE` | `/apps/{app_id}/flags/{flag_key}` | Delete flag |207| `GET` | `/apps/{app_id}/flags/{flag_key}/changelog?limit=20&cursor=<cursor>` | Flag changelog |208209### Evaluate Endpoint210211```212GET /apps/{app_id}/evaluate?flagKey=<key>&<context-attrs>213```214215Requires an API token with the `com.cloudflare.account.flagship.evaluate` permission. Context attributes passed as query params. This endpoint is not wrapped in the management envelope; the SDK contract returns OpenFeature-style camelCase:216217```json218{219"flagKey": "my-flag",220"value": true,221"variant": "on",222"reason": "SPLIT"223}224```225226Reasons: `TARGETING_MATCH`, `SPLIT`, `DEFAULT`, `DISABLED`.227228### Management Response Payloads229230Management endpoints are wrapped in the Cloudflare v4 envelope shown above. Common `.result` payloads:231232**App result**233234```json235{236"id": "app-uuid",237"name": "my-app",238"created_at": "2026-06-09T12:00:00.000Z",239"updated_at": "2026-06-09T12:00:00.000Z",240"updated_by": "[email protected]"241}242```243244**Flag result**245246```json247{248"key": "my-flag",249"type": "boolean",250"default_variation": "off",251"variations": { "on": true, "off": false },252"rules": [],253"description": "Enables the new feature",254"enabled": true,255"updated_at": "2026-06-09T12:00:00.000Z",256"updated_by": "[email protected]"257}258```259260**Changelog entry**261262```json263{264"flag_key": "my-flag",265"event": "update",266"after": { "key": "my-flag", "default_variation": "off", "variations": { "on": true, "off": false }, "rules": [], "enabled": true },267"diff": { "enabled": { "from": false, "to": true } }268}269```270271Changelog entries include the full flag state after the change. `update` entries also include `diff`.272273---274275## FlagDefinition Schema276277```json278{279"key": "my-flag",280"type": "boolean",281"default_variation": "off",282"variations": {283"on": true,284"off": false285},286"rules": [287{288"priority": 1,289"conditions": [290{291"attribute": "email",292"operator": "ends_with",293"value": "@cloudflare.com"294}295],296"serve_variation": "on",297"rollout": { "percentage": 100 }298}299],300"description": "Enables the new feature",301"enabled": true302}303```304305### Field Constraints306307| Field | Type | Constraints |308|-------|------|-------------|309| `key` | string | 1-64 chars, `/^[a-zA-Z0-9_-]+$/` |310| `type` | enum | Optional. `boolean`, `string`, `number`, `json` (auto-inferred from variations) |311| `default_variation` | string | Must be a key in `variations` |312| `variations` | `Record<string, T>` | At least one. All values same type. Keys: alphanumeric/hyphens/underscores, max 64 chars. Values max 10KB. |313| `rules` | `Rule[]` | Can be empty. No duplicate priorities. |314| `description` | string? | Max 512 chars, nullable |315| `enabled` | boolean | Required. `false` = always returns default variation. |316317### Rule Schema318319```json320{321"priority": 1,322"conditions": [ /* Condition[] */ ],323"serve_variation": "on",324"rollout": { "percentage": 50, "attribute": "targetingKey" }325}326```327328- `priority`: integer >= 1, unique across rules in the flag (lower = evaluated first)329- `conditions`: array of base or logical conditions330- `serve_variation`: must be a key in `variations`331- `rollout`: optional. `percentage` 0-100. `attribute` defaults to `targetingKey`.332333### Condition Schema334335**Base condition:**336337```json338{ "attribute": "email", "operator": "ends_with", "value": "@cloudflare.com" }339```340341**Logical condition (AND/OR):**342343```json344{345"logical_operator": "AND",346"clauses": [347{ "attribute": "country", "operator": "equals", "value": "US" },348{ "attribute": "plan", "operator": "in", "value": ["enterprise", "business"] }349]350}351```352353Nesting supported up to 6 levels deep.354355### Operators356357| Operator | Description | Value Type |358|----------|-------------|------------|359| `equals` | Exact match (case-sensitive) | String |360| `not_equals` | Not exact match | String |361| `greater_than` | Numeric / datetime > | Number, ISO 8601 |362| `less_than` | Numeric / datetime < | Number, ISO 8601 |363| `greater_than_or_equals` | >= | Number, ISO 8601 |364| `less_than_or_equals` | <= | Number, ISO 8601 |365| `contains` | Substring match (case-sensitive) | String |366| `starts_with` | Prefix match | String |367| `ends_with` | Suffix match | String |368| `in` | Value in array | Array |369| `not_in` | Value not in array | Array |370371---372373## Rate Limits374375| Operation | Limit |376|-----------|-------|377| Mutations (POST/PUT/DELETE) | 60 per 60s per account:app |378| Reads (GET) | 600 per 60s per account:app |379380## Error Codes381382| HTTP Status | Meaning |383|-------------|---------|384| 200 | Success (read/update/delete) |385| 201 | Created (create) |386| 400 | Validation error (check `errors[].message`) |387| 401 | Invalid or missing token |388| 404 | Flag or app not found |389| 409 | Flag key already exists (create) |390| 429 | Rate limited |391