Introduction
FlagDash is a feature flag and remote configuration platform for modern development teams. Deploy safely, test in production, and roll out gradually without redeploying code.
Feature Flags
Boolean flags with per-environment states and targeting rules.
Remote Config
JSON-powered configuration that updates instantly across all your environments.
Multi-Environment
Separate flag states for dev, staging, and production with independent API keys.
Architecture
FlagDash uses two types of credentials:
- sk_ API Key Project + environment scoped. Configurable permissions — read-only for frontends, or read-write for backend services.
- pat_ PAT Personal Access Token. User-scoped with full account access. For CLI, management API, and automation. Never expose in frontend code.
Authentication
API requests authenticate via API keys in the Authorization header. The MCP server and CLI also support OAuth 2.1 browser-based login.
MCP & CLI Authentication
The MCP server supports OAuth 2.1
with PKCE for browser-based login.
MCP clients (Claude Code, Cursor, Windsurf) handle the flow automatically.
The CLI uses device authorization — run flagdash
and approve the login in your browser.
Credential Types
Create API Keys
(sk_) from the
API Keys
page — these are scoped to a specific project and environment with configurable permissions.
Create Personal Access Tokens
(pat_) from
Settings > Personal Access Tokens
— these are user-scoped with full account access.
Authorization: Bearer YOUR_API_KEY
Which credential do I need?
An sk_
API Key is enough for most use cases.
All SDKs (including Go, Python, and Node.js) work with API keys
to read evaluated flag values, config values, and AI configs.
You only need a pat_
Personal Access Token for CLI login, management API operations, and account-wide automation.
| Credential | Prefix | Scope | Permissions | Use case |
|---|---|---|---|---|
| API Key | sk_ | Project + environment | Configurable scopes (flags:read, configs:write, etc.) | SDKs, frontend, backend services |
| Personal Access Token | pat_ | User-scoped, account-wide | Full access to all projects and resources | CLI, management API, automation |
Base URL
https://your-flagdash-instance.com/api/v1
Rate Limits
Credentials have per-minute rate limits:
| Credential | Rate Limit | Scope |
|---|---|---|
| sk_ | 1,000 req/min | Read/write per configured scopes |
| pat_ | 100 req/min | Full access to all resources |
Quickstart
Get up and running with FlagDash in under 5 minutes.
1. Create a project
Sign in to the dashboard and create a new project. Default environments will be created automatically based on your plan.
2. Create a feature flag
Go to the Flags page and create your first flag. Choose a key (e.g. new-checkout) and type.
3. Generate an API key
Go to API Keys and generate an API Key
(sk_) for your project and environment.
4. Evaluate in your app
import { FlagDash } from '@flagdash/sdk';
const client = FlagDash.init({
sdkKey: 'sk_...',
});
// Read a flag value
const enabled = await client.flag('new-checkout');
if (enabled) {
showNewCheckout();
}
import flagdash "github.com/flagdash/flagdash-go"
client, _ := flagdash.NewClient("sk_...")
enabled, _ := client.Flag(ctx, "new-checkout", nil, false)
if enabled.(bool) {
showNewCheckout()
}
from flagdash import FlagDashClient
with FlagDashClient(sdk_key="sk_...") as client:
enabled = client.flag("new-checkout", default=False)
if enabled:
show_new_checkout()
# Evaluate all flags
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://your-app.com/api/v1/flags
# Evaluate a single flag
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://your-app.com/api/v1/flags/new-checkout
# Evaluate with user context (targeting + rollout)
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://your-app.com/api/v1/flags/new-checkout?user_id=usr_123&plan=pro"
Flag Evaluation
How FlagDash decides what value a feature flag returns. Understanding the evaluation pipeline is key to using targeting rules, rollouts, and A/B testing effectively.
Evaluation Order
When a flag is evaluated, FlagDash checks conditions in this exact order. The first match wins — once a condition is met, evaluation stops and returns that value.
Disabled check
If the flag is disabled (toggled OFF in the dashboard),
return the default_value immediately.
Nothing else is checked. Reason: disabled
Targeting rules
If any targeting rule matches the user context, return that rule's variation value.
Rules are checked in order — the first matching rule wins.
Reason: rule_match
A/B Variations
If variations are configured, select one using weighted consistent hashing
(salted with the flag ID to decorrelate across tests).
Reason: variation
Percentage rollout
If the user falls within the rollout percentage, return the flag's active value.
Uses consistent hashing on user_id so the same user
always gets the same result.
Reason: rollout
Default
If nothing else matched, return the default_value.
Reason: default
Targeting Rules
Targeting rules let you return different values to different users based on context attributes. Each rule has an attribute, an operator, values to compare against, and a variation value to return when matched.
How to set up a targeting rule
- Go to the flag detail page in the dashboard
- Make sure the flag is enabled (toggled ON)
- In the Targeting tab, click + Add Rule
-
Set the Attribute
(e.g.
country) — this must match a context key you send from your app -
Choose an Operator
(e.g.
eq) -
Enter Values
(e.g.
US) — comma-separated for multiple values - Click Save Rules
Now when your app sends country=US as context,
the flag returns the rule's variation value instead of the default.
# Without context — returns default (0% rollout = false)
curl -s -H "Authorization: Bearer YOUR_API_KEY" \
"https://your-app.com/api/v1/flags/my-flag"
# → {"key":"my-flag","value":false,"reason":"default"}
# With context — targeting rule matches country=US → true
curl -s -H "Authorization: Bearer YOUR_API_KEY" \
"https://your-app.com/api/v1/flags/my-flag?country=US"
# → {"key":"my-flag","value":true,"reason":"rule_match"}
Supported operators
| Operator | Description | Example |
|---|---|---|
| eq | Equals (value in list) | country eq [US, CA] |
| neq | Not equals | plan neq [free] |
| contains | String contains substring | email contains [@company.com] |
| starts_with | String starts with | email starts_with [admin] |
| ends_with | String ends with | email ends_with [.edu] |
| in | Value in list | tier in [pro, enterprise] |
| not_in | Value not in list | tier not_in [free] |
| gt / lt / gte / lte | Numeric comparison | age gt [18] |
| regex | Regex pattern match | email regex [^.*@acme\.com$] |
Percentage Rollout
Rollout percentage controls what fraction of users see the flag's active value.
It uses consistent hashing
on the user_id
context key, so:
- The same
user_idalways gets the same result - Increasing from 10% to 25% adds new users without removing existing ones
- 100% rollout = all users get the flag value
- 0% rollout = no users get the flag value (falls through to default)
Important:
Without a user_id
in the context, rollouts are non-deterministic
— a random value is generated each evaluation.
Always send a stable identifier for consistent results.
Rollout + targeting rules
Targeting rules are checked before rollout. A common pattern is to set rollout to 0% and use targeting rules to enable the flag for specific users:
- Set rollout to 0% (nobody gets it by default)
-
Add a targeting rule:
country eq US→true -
Only users with
country=USsee the flag astrue - Everyone else sees
false(default value)
User Context
Context is how you tell FlagDash about the current user. It's a key-value map sent as query parameters on the API call, or passed directly in SDKs.
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://your-app.com/api/v1/flags/checkout-v2?user_id=alice&country=US&plan=pro"
// Browser SDK (client-side)
const value = await client.flag('checkout-v2', {
user: { id: 'alice' },
country: 'US',
plan: 'pro',
});
// With full detail (value + reason + variation key)
const detail = await client.flagDetail('checkout-v2', {
user: { id: 'alice' },
country: 'US',
});
// → { key: 'checkout-v2', value: true, reason: 'rule_match', variationKey: null }
ctx := context.Background()
// Simple evaluation with context
value, err := client.Flag(ctx, "checkout-v2", flagdash.EvaluationContext{
"user_id": "alice",
"country": "US",
"plan": "pro",
}, false)
// Full detail (value + reason + variation key)
detail, err := client.FlagDetail(ctx, "checkout-v2", flagdash.EvaluationContext{
"user_id": "alice",
"country": "US",
})
// detail.Value = true, detail.Reason = "rule_match"
from flagdash import FlagDashClient, EvaluationContext
client = FlagDashClient(sdk_key="sk_...")
# Simple evaluation with context
value = client.flag(
"checkout-v2",
default=False,
context=EvaluationContext(user_id="alice", country="US", plan="pro"),
)
# Full detail (value + reason + variation key)
detail = client.flag_detail(
"checkout-v2",
context=EvaluationContext(user_id="alice", country="US"),
)
# detail.value = True, detail.reason = "rule_match"
| Context Key | Purpose | Used For |
|---|---|---|
| user_id | Stable user identifier | Consistent rollouts, A/B bucketing |
| User email | Targeting rules (e.g. internal users) | |
| country | Country code | Geo-targeting rules |
| plan | Subscription plan | Plan-based feature gating |
| any key | Custom attribute | Any key-value pair works for targeting |
Response Format
The single flag endpoint returns the evaluated value plus the
reason
explaining why that value was chosen.
{
"key": "checkout-v2",
"value": true,
"reason": "rule_match"
}
{
"key": "checkout-v2",
"value": true,
"reason": "rollout"
}
{
"key": "checkout-v2",
"value": "variant-b",
"reason": "variation",
"variation_key": "b"
}
{
"key": "checkout-v2",
"value": false,
"reason": "default"
}
| Reason | Meaning |
|---|---|
| disabled | Flag is toggled OFF — always returns default value |
| rule_match | A targeting rule matched the user context |
| variation | Selected via A/B variation (weighted consistent hashing) |
| rollout | User fell within the rollout percentage |
| default | No rules, variations, or rollout matched |
JavaScript / TypeScript SDK
Client-side SDK for browser environments. Provides flag evaluation, remote config, and AI config access with real-time SSE updates and polling fallback.
Installation
npm install @flagdash/sdk
Initialize
import { FlagDash } from '@flagdash/sdk';
const client = FlagDash.init({
sdkKey: 'sk_...', // Your API key
baseUrl: 'https://your-app.com', // Your FlagDash instance URL
realtime: true, // SSE for instant updates (optional)
// refreshInterval: 30000, // Or use polling instead (optional)
});
Evaluate Flags
// Boolean flag (uses cached value from initial GET /flags)
const showBanner = await client.flag('show-banner');
// Flag with user context — server evaluates targeting rules
// and percentage rollout via GET /flags/:key?user_id=...
const newUI = await client.flag('new-ui', {
user: { id: 'usr_123', email: 'john@acme.com', plan: 'premium' }
});
// All flags with context (single request)
const flags = await client.allFlags({
user: { id: 'usr_123' }, country: 'US'
});
// String flag with default value
const theme = await client.flag('app-theme', {}, 'light');
Read Remote Config
// Get a remote config value
const pricing = await client.config('pricing-tiers');
console.log(pricing); // { basic: 9.99, pro: 29.99 }
// Config with default value
const limits = await client.config('rate-limits', { default: 100 });
AI Configs
// Get a single AI config file
const agent = await client.aiConfig('agent.md');
console.log(agent.content); // "# Agent Instructions..."
console.log(agent.file_type); // "agent"
console.log(agent.folder); // null or "prompts"
// List all AI configs
const configs = await client.listAiConfigs();
// Filter by file type
const skills = await client.listAiConfigs({ fileType: 'skill' });
// Filter by folder
const prompts = await client.listAiConfigs({ folder: 'prompts' });
Flag Detail
Get detailed evaluation information for a flag, including the reason it resolved to its value and the variation key (if applicable). This always calls the server for accurate evaluation data.
// Get detailed evaluation info (always calls the server)
const detail = await client.flagDetail('new-checkout', {
user: { id: 'usr_123', plan: 'premium' }
});
console.log(detail.key); // "new-checkout"
console.log(detail.value); // true
console.log(detail.reason); // "rule_match" | "variation" | "rollout" | "disabled" | "default"
console.log(detail.variationKey); // "variant-a" (null if not a variation)
// With a default value
const theme = await client.flagDetail('app-theme', {}, 'light');
// theme = { key: "app-theme", value: "dark", reason: "rollout", variationKey: null }
All Configs
Retrieve all remote config values from cache in a single call, similar to allFlags().
// Get all config values from cache (single call)
const configs = await client.allConfigs();
console.log(configs);
// { "pricing-tiers": { basic: 9.99, pro: 29.99 }, "rate-limits": { default: 100 } }
Realtime Controls
Enable or disable SSE real-time updates at runtime without re-initializing the client.
// Check current SSE state
console.log(client.isRealtimeEnabled); // true or false
// Disable SSE at runtime (closes the connection)
client.disableRealtime();
// Re-enable SSE at runtime (opens a new connection)
client.enableRealtime();
// Listen for realtime state changes
client.on('realtime_changed', (enabled) => {
console.log('Realtime is now:', enabled ? 'on' : 'off');
});
Events
// Listen for events
client.on('ready', () => console.log('FlagDash ready'));
client.on('flags_updated', (flags) => console.log('Flags changed:', flags));
client.on('config_updated', () => console.log('Config changed'));
client.on('configs_updated', (configs) => console.log('Configs changed:', configs));
client.on('ai_config_updated', () => console.log('AI config changed'));
client.on('realtime_changed', (enabled) => console.log('Realtime:', enabled));
client.on('error', (err) => console.error('Error:', err));
// Clean up when done (closes SSE connection + stops polling)
client.destroy();
React SDK
React hooks and components for feature flags, remote config, and AI configs.
Built on top of @flagdash/sdk with SSR-safe defaults.
Installation
npm install @flagdash/react @flagdash/sdk
Provider Setup
import { FlagDashProvider } from '@flagdash/react';
function App() {
return (
<FlagDashProvider
sdkKey="sk_..."
baseUrl="https://your-app.com"
user={{ id: 'usr_123', plan: 'premium' }}
realtime // SSE for instant updates (optional)
>
<MyApp />
</FlagDashProvider>
);
}
useFlag Hook
import { useFlag } from '@flagdash/react';
function CheckoutButton() {
const showNewCheckout = useFlag('new-checkout', false);
if (!showNewCheckout) return <OldCheckout />;
return <NewCheckout />;
}
useFlagDetail Hook
Get detailed evaluation information for a flag, including the reason it resolved to its value and the variation key. Always calls the server for accurate data.
import { useFlagDetail } from '@flagdash/react';
function FeatureBanner() {
const {
value,
reason,
variationKey,
isLoading,
} = useFlagDetail('new-checkout', false, { user_id: 'usr_123' });
if (isLoading) return <Skeleton />;
// reason: "disabled" | "rule_match" | "variation" | "rollout" | "default"
return (
<div>
<p>Value: {String(value)}</p>
<p>Reason: {reason}</p>
{variationKey && <p>Variation: {variationKey}</p>}
</div>
);
}
useConfig Hook
The useConfig
and useConfigWithLoading
hooks are reactive to SSE updates. When realtime is enabled,
config values automatically refresh when changes are pushed from the server.
import { useConfig, useFlagDash } from '@flagdash/react';
function PricingDisplay() {
const pricing = useConfig('pricing-tiers', { basic: 9.99 });
const { isReady } = useFlagDash();
if (!isReady) return <Skeleton />;
return <span>${pricing.basic}/mo</span>;
}
AI Config Hooks
import { useAiConfig, useAiConfigs } from '@flagdash/react';
function AgentInstructions() {
const { content, fileType, isLoading } = useAiConfig('agent.md');
if (isLoading) return <Spinner />;
return <pre>{content}</pre>;
}
function SkillList() {
const { configs, isLoading } = useAiConfigs({ fileType: 'skill' });
if (isLoading) return <Spinner />;
return (
<ul>
{configs.map(c => <li key={c.file_name}>{c.file_name}</li>)}
</ul>
);
}
Available Hooks
| Hook | Returns | Description |
|---|---|---|
| useFlag(key, default) | T | Evaluate a feature flag reactively |
| useFlagWithLoading(key, default) | { value, isLoading } | Flag evaluation with loading state |
| useFlagDetail(key, default, ctx?) | { value, reason, variationKey, isLoading } | Flag evaluation with reason and variation details |
| useConfig(key, default) | T | Get a remote config value reactively (auto-refreshes via SSE) |
| useConfigWithLoading(key, default) | { value, isLoading } | Config value with loading state (auto-refreshes via SSE) |
| useAiConfig(fileName) | { content, fileName, fileType, folder, isLoading } | Get an AI config file reactively |
| useAiConfigs(options?) | { configs, isLoading } | List AI config files with optional filters |
| useFlagDash() | { client, isReady } | Access the raw client and readiness state |
Error Boundary
Catch errors from FlagDash hooks with FlagDashErrorBoundary:
import { FlagDashErrorBoundary } from '@flagdash/react';
// With static fallback
<FlagDashErrorBoundary fallback={<div>Failed to load flags</div>}>
<MyComponent />
</FlagDashErrorBoundary>
// With render function for retry
<FlagDashErrorBoundary
fallback={(error, reset) => (
<div>
<p>Error: {error.message}</p>
<button onClick={reset}>Retry</button>
</div>
)}
onError={(error) => console.error('FlagDash error:', error)}
>
<MyComponent />
</FlagDashErrorBoundary>
Node.js (Server SDK)
Server-side SDK with TTL caching, full flag details, and a management client for CRUD operations.
Works with sk_
API Keys.
A key with read scopes is sufficient for reading flag values, configs, and AI configs.
Use a key with write scopes for CRUD operations, or a pat_
Personal Access Token for account-wide management.
Installation
npm install @flagdash/node
Server Client
const { FlagDashServer } = require('@flagdash/node');
const client = FlagDashServer.init({
sdkKey: 'sk_...', // Your API key
baseUrl: 'https://your-app.com', // Your FlagDash instance URL
cacheTTL: 60000, // Cache for 60s (default)
});
// Evaluate a flag with user context
app.get('/checkout', async (req, res) => {
const newCheckout = await client.flag('new-checkout', {
user: { id: req.userId, plan: req.userPlan }
});
if (newCheckout) {
return res.render('checkout-v2');
}
return res.render('checkout');
});
// Get all evaluated flags for a user
const flags = await client.allFlags({
user: { id: req.userId }
});
// flags = { "new-checkout": true, "dark-mode": false }
// Get a flag with full rule details
const flagDetails = await client.getFlag('new-checkout');
console.log(flagDetails.rollout_percentage);
Flag Detail
Get detailed evaluation information including the reason a flag resolved to its value and the variation key. Always calls the server for accurate evaluation data.
// Get detailed evaluation info (always calls the server)
const detail = await client.flagDetail('new-checkout', {
user: { id: req.userId, plan: req.userPlan }
});
console.log(detail.key); // "new-checkout"
console.log(detail.value); // true
console.log(detail.reason); // "rule_match" | "variation" | "rollout" | "disabled" | "default"
console.log(detail.variationKey); // "variant-a" (null if not a variation)
// Use for analytics or debugging
analytics.track('flag_evaluated', {
flag: detail.key,
value: detail.value,
reason: detail.reason,
});
AI Configs
// Get a single AI config file (with caching)
const agent = await client.aiConfig('agent.md');
console.log(agent.content); // Markdown content
console.log(agent.file_type); // "agent"
// List all AI config files
const configs = await client.listAiConfigs();
const skills = configs.filter(c => c.file_type === 'skill');
Management Client
The management client provides full CRUD operations for flags, configs, AI configs, and webhooks.
Requires a pat_
Personal Access Token or an API key with write scopes.
const { FlagDashManagement } = require('@flagdash/node');
const mgmt = FlagDashManagement.init({
apiKey: 'pat_...',
baseUrl: 'https://your-app.com',
});
// Create a flag
const flag = await mgmt.createFlag({
project_id: 'prj_xxx',
key: 'new-feature',
name: 'New Feature',
flag_type: 'boolean',
});
// Toggle a flag in an environment
await mgmt.toggleFlag('new-feature', 'prj_xxx', 'env_xxx');
// Set rollout percentage
await mgmt.updateFlagRollout('new-feature', 'prj_xxx', 'env_xxx', 25);
// Create an AI config file
await mgmt.createAiConfig({
project_id: 'prj_xxx',
environment_id: 'env_xxx',
file_name: 'agent.md',
file_type: 'agent',
content: '# Agent Instructions\n\n...',
});
Go SDK
Go client for FlagDash with thread-safe TTL caching and zero external dependencies.
Use an sk_
API Key for reading flag values, configs, and AI configs.
The SDK routes to the correct endpoints based on the key's configured scopes.
Use a pat_
Personal Access Token for management operations.
Installation
go get github.com/flagdash/flagdash-go
Quick Start
package main
import (
"context"
"fmt"
"log"
"time"
flagdash "github.com/flagdash/flagdash-go"
)
func main() {
client, err := flagdash.NewClient(
"sk_...",
flagdash.WithBaseURL("https://your-app.com"),
flagdash.WithCacheTTL(60 * time.Second),
)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
enabled, err := client.Flag(ctx, "new-checkout", nil, false)
if err != nil {
log.Printf("flag error: %v", err)
}
fmt.Println("new-checkout:", enabled)
}
Feature Flags
ctx := context.Background()
// Simple flag evaluation (cached)
enabled, _ := client.Flag(ctx, "dark-mode", nil, false)
// Flag with user context (not cached)
value, _ := client.Flag(ctx, "new-checkout",
flagdash.EvaluationContext{
"user_id": "usr_123",
"plan": "premium",
},
false,
)
// Get all evaluated flags
flags, _ := client.AllFlags(ctx, nil)
fmt.Println(flags["dark-mode"]) // true
// Get flag with full details (requires read scopes)
flag, _ := client.GetFlag(ctx, "new-checkout")
fmt.Println(flag.RolloutPercentage) // 50
Flag Detail
Get detailed evaluation information including the reason a flag resolved to its value
and the variation key. Returns a *FlagDetailResult
struct. Always calls the server for accurate evaluation data.
ctx := context.Background()
// Get detailed evaluation info (always calls the server)
detail, err := client.FlagDetail(ctx, "new-checkout",
flagdash.EvaluationContext{
"user_id": "usr_123",
"plan": "premium",
},
)
if err != nil {
log.Printf("flag detail error: %v", err)
}
fmt.Println(detail.Key) // "new-checkout"
fmt.Println(detail.Value) // true
fmt.Println(detail.Reason) // "rule_match" | "variation" | "rollout" | "disabled" | "default"
fmt.Println(detail.VariationKey) // "variant-a" (empty string if not a variation)
Remote Config
// Get a config value (cached)
pricing, _ := client.Config(ctx, "pricing-tiers", nil)
// Get all configs
configs, _ := client.AllConfigs(ctx)
for _, c := range configs {
fmt.Printf("%s = %v\n", c.Key, c.Value)
}
AI Configs
// Get a single AI config file
agent, err := client.AiConfig(ctx, "agent.md")
if err != nil {
log.Fatal(err)
}
fmt.Println(agent.Content) // "# Agent Instructions..."
fmt.Println(agent.FileType) // "agent"
// List all AI configs
configs, _ := client.ListAiConfigs(ctx)
for _, c := range configs {
fmt.Printf("[%s] %s (%s)\n", c.FileType, c.FileName, c.Folder)
}
Options
| Option | Default | Description |
|---|---|---|
| WithBaseURL(url) | https://flagdash.io | Set the FlagDash instance URL |
| WithCacheTTL(d) | 60s | Cache TTL (0 to disable) |
| WithTimeout(d) | 5s | HTTP request timeout |
Python SDK
Python client for FlagDash with both sync and async support.
Uses httpx for HTTP requests and supports context managers.
Use FlagDashClient
with an sk_
API Key
for reading flag values, configs, and AI configs.
Use ServerClient
with an API key that has full read scopes
for flag details (rules, variations, rollout metadata).
Installation
pip install flagdash
Client SDK
from flagdash import FlagDashClient, EvaluationContext
# As a context manager (recommended)
with FlagDashClient(
sdk_key="sk_...",
base_url="https://your-app.com",
) as client:
# Evaluate a flag
enabled = client.flag("new-checkout", default=False)
# With user context for targeting
value = client.flag(
"new-checkout",
context=EvaluationContext(
user_id="usr_123",
attributes={"plan": "premium"},
),
)
# Get all flags
flags = client.all_flags()
print(flags) # {"new-checkout": True, "dark-mode": False}
# Get a remote config
pricing = client.config("pricing-tiers", default={})
Server SDK
from flagdash import FlagDashServerClient, EvaluationContext
with FlagDashServerClient(
sdk_key="sk_...",
base_url="https://your-app.com",
cache_ttl=60.0, # Cache for 60 seconds (default)
) as client:
# Evaluate a flag (cached)
enabled = client.flag("new-checkout", default=False)
# Get flag with full metadata
flag = client.get_flag("new-checkout")
print(flag.rollout_percentage) # 50
print(flag.variations) # [FlagVariation(...), ...]
# List all flags with details
flags = client.list_flags()
Flag Detail
Get detailed evaluation information including the reason a flag resolved to its value
and the variation key. Returns a FlagDetailResult
with key, value, reason, and
variation_key
fields. Both sync and async clients support this method.
from flagdash import FlagDashClient, EvaluationContext
# Sync client
with FlagDashClient(sdk_key="sk_...", base_url="https://your-app.com") as client:
detail = client.flag_detail(
"new-checkout",
context=EvaluationContext(
user_id="usr_123",
attributes={"plan": "premium"},
),
)
print(detail.key) # "new-checkout"
print(detail.value) # True
print(detail.reason) # "rule_match" | "variation" | "rollout" | "disabled" | "default"
print(detail.variation_key) # "variant-a" (None if not a variation)
# With a default value
theme = client.flag_detail("app-theme", default="light")
# Async client
from flagdash import FlagDashAsyncClient
async with FlagDashAsyncClient(sdk_key="sk_...") as client:
detail = await client.flag_detail("new-checkout")
print(detail.reason) # "default"
AI Configs
# Read-only access (content only)
agent = client.ai_config("agent.md")
print(agent.content) # "# Agent Instructions..."
print(agent.file_type) # "agent"
configs = client.list_ai_configs()
skills = [c for c in configs if c.file_type == "skill"]
# Full metadata with caching
from flagdash import FlagDashServerClient
with FlagDashServerClient(sdk_key="sk_...") as server:
agent = server.ai_config("agent.md")
print(agent.id) # "aic_xxxx"
print(agent.is_active) # True
print(agent.folder) # None or "prompts"
Async Support
import asyncio
from flagdash import FlagDashAsyncClient
async def main():
async with FlagDashAsyncClient(
sdk_key="sk_...",
base_url="https://your-app.com",
) as client:
enabled = await client.flag("new-checkout", default=False)
configs = await client.list_ai_configs()
print(f"Found {len(configs)} AI configs")
asyncio.run(main())
Available Clients
| Class | Key Type | Description |
|---|---|---|
| FlagDashClient | sk_ | Read-only: evaluated flags, configs, AI configs |
| FlagDashServerClient | sk_ | Full metadata with TTL cache |
| FlagDashAsyncClient | sk_ | Async version of FlagDashClient |
| FlagDashAsyncServerClient | sk_ | Async version of FlagDashServerClient |
| FlagDashManagementClient | pat_ | Full CRUD: flags, configs, AI configs, webhooks |
CLI / TUI
Interactive terminal UI for managing feature flags, remote configs, AI configs, webhooks, and environments. Browse and edit your FlagDash resources without leaving the terminal.
Installation
brew install flagdash/tap/flagdash
curl -fsSL https://flagdash.io/install.sh | sh
npm install -g @flagdash/cli
cargo install flagdash-cli
Quick Start
Run flagdash
to launch the TUI. On first run, the CLI will open your browser for authentication
using a device authorization flow. Approve the login, then select a project and environment
to work with. Your session is saved for future use.
# Launch the TUI (opens browser for login on first run)
flagdash
# Or pass a PAT directly (skips browser auth)
flagdash --api-key pat_xxx --project-id prj_xxx
Navigation
The TUI is fully keyboard-driven. Use the shortcuts below to navigate between screens and manage resources.
Global
| Key | Action |
|---|---|
| 1-6 | Switch sections (Dashboard, Flags, Configs, AI Configs, Webhooks, Environments) |
| j/k / arrows | Navigate lists |
| Enter | Open detail view |
| Esc | Go back |
| / | Search (flags, configs, AI configs) |
| q | Quit |
List Views
| Key | Action |
|---|---|
| c | Create new resource |
| d | Delete selected |
| t | Toggle flag (flags list only) |
Detail Views
| Key | Action |
|---|---|
| e | Edit resource |
| t | Toggle flag on/off (flags only) |
| r | Edit rollout percentage (flags only) |
| u | Edit targeting rules (flags only) |
| v | Edit variations (flags) / Edit value (configs) |
| s | View schedules (flags only) |
Screens
| Screen | Description |
|---|---|
| Dashboard | Overview of projects, flags, configs, and recent activity |
| Flags | List, toggle, edit rollout, manage targeting rules, A/B variations, and schedules |
| Configs | View and edit remote config values per environment |
| AI Configs | Manage AI config files (agent, skills, rules) with markdown preview |
| Webhooks | View webhook endpoints and delivery logs |
| Environments | Browse projects and environments, switch active context |
Configuration
Configuration is loaded in this priority order (highest wins):
-
CLI arguments (
--api-key,--project-id) - Environment variables
-
Config file (
~/.config/flagdash/config.toml)
[auth]
# Session token (issued after browser login)
session_token = "session_xxx"
# Or use a Personal Access Token instead
# api_key = "pat_xxx"
[connection]
base_url = "https://flagdash.io"
[defaults]
project_id = "prj_xxx"
environment_id = "env_xxx"
| Environment Variable | Description |
|---|---|
| FLAGDASH_API_KEY | Personal Access Token (pat_) or API key (sk_) |
| FLAGDASH_BASE_URL | FlagDash instance URL (default: https://flagdash.io) |
| FLAGDASH_PROJECT_ID | Default project ID |
| FLAGDASH_ENVIRONMENT_ID | Default environment ID |
Feature Flags API
Read and evaluate feature flags via the REST API. All flag endpoints support server-side evaluation through context query parameters.
Context Query Parameters
Pass user context as query parameters to enable server-side targeting and percentage rollouts. The server evaluates rules and consistent hashing on your behalf.
| Param | Description | Example |
|---|---|---|
| user_id | Stable user ID for consistent rollouts | ?user_id=alice |
| country | Country code for geo-targeting | ?country=US |
| plan | Subscription plan for plan-based rules | ?plan=pro |
| any key | Any key=value pair is forwarded to the evaluator | ?beta=true |
Evaluation priority:
disabled flag → targeting rules → percentage rollout → default value.
Without a user_id, rollouts are non-deterministic.
Client Endpoints
/api/v1/flags
Evaluate all flags for the current environment (requires flags:read scope)
{
"flags": {
"new-checkout": true,
"dark-mode": false,
"max-cart-items": 50
}
}
/api/v1/flags/:key
Evaluate a single flag by key (requires flags:read scope)
{
"key": "new-checkout",
"value": true,
"reason": "rollout"
}
# Evaluate flags with user context
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://your-app.com/api/v1/flags?user_id=usr_123&country=US"
# Evaluate a single flag with context
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://your-app.com/api/v1/flags/new-checkout?user_id=usr_123&plan=pro"
Server Endpoints
Server endpoints return full flag configuration (rules, rollout, value) plus
an evaluated_value
per flag and a top-level evaluated
map for quick lookups.
/api/v1/server/flags
List all flags with rules + evaluated values (requires flags:read scope)
{
"flags": [
{
"key": "new-checkout",
"name": "New Checkout Flow",
"flag_type": "boolean",
"enabled": true,
"value": {"value": true},
"rules": {"rules": []},
"rollout_percentage": 100,
"evaluated_value": true
}
],
"evaluated": {
"new-checkout": true
}
}
/api/v1/server/flags/:key
Get a flag with full details + evaluated value (requires flags:read scope)
{
"flag": {
"key": "new-checkout",
"name": "New Checkout Flow",
"flag_type": "boolean",
"enabled": true,
"value": {"value": true},
"rules": {"rules": []},
"rollout_percentage": 100,
"evaluated_value": true
}
}
Remote Config API
Read and manage remote configuration values.
Client Endpoints
/api/v1/configs
List all configs for the current environment (requires configs:read scope)
{
"configs": [
{"key": "pricing-tiers", "value": {"basic": 9.99, "pro": 29.99}},
{"key": "rate-limits", "value": {"default": 100}}
]
}
/api/v1/configs/:key
Get a config value by key (requires configs:read scope)
{
"key": "pricing-tiers",
"value": {
"basic": 9.99,
"pro": 29.99,
"enterprise": null
},
"config_type": "json"
}
Server Endpoints
/api/v1/server/configs
List all configs (requires configs:read scope)
/api/v1/server/configs/:key
Get a config with full metadata (requires configs:read scope)
AI Config API
Manage per-environment AI/LLM context files: agent instructions, skills, and rules.
File Types
| Type | Description | Limit |
|---|---|---|
| agent | Agent behavior instructions | 1 per environment |
| skill | Skill definitions for the AI agent | Multiple allowed |
| rule | Rules and constraints for agent behavior | Multiple allowed |
Maximum 10 files
per environment.
File names must end in .md
and use lowercase letters, numbers, dots, hyphens.
Content is limited to 50,000 characters
per file.
Files can be organized into folders
for visual grouping
(folder names use lowercase letters, numbers, dots, hyphens, and underscores).
Client Endpoints
/api/v1/ai-configs
List all active AI config files (requires ai_configs:read scope)
{
"ai_configs": [
{
"file_name": "agent.md",
"file_type": "agent",
"content": "# Agent Instructions...",
"folder": null
},
{
"file_name": "example-skill.md",
"file_type": "skill",
"content": "# Example Skill...",
"folder": "prompts"
}
]
}
/api/v1/ai-configs/:file_name
Get a single active AI config file (requires ai_configs:read scope)
Server Endpoints
/api/v1/server/ai-configs
List all AI config files with metadata (requires ai_configs:read scope)
/api/v1/server/ai-configs/:file_name
Get a file with full metadata (requires ai_configs:read scope)
Management Endpoints
/api/v1/manage/ai-configs
List files (requires project_id + environment_id query params)
/api/v1/manage/ai-configs
Create a new AI config file (supports optional folder param)
/api/v1/manage/ai-configs/initialize
Initialize default template files for an environment
/api/v1/manage/ai-configs/:file_name
Update file content and/or folder
/api/v1/manage/ai-configs/:file_name
Delete a file
MCP Integration
AI config files are exposed via the MCP server as the flagdash://ai-configs
resource. The server instructions
tell AI agents to read this resource first, loading agent instructions,
skills, and rules into context before processing requests.
Environments
Environments let you maintain separate flag states and config values for different stages of your deployment pipeline.
How it works
Each API key is scoped to a specific environment. When you evaluate a flag or read a config, the response is specific to that environment. This means you can have a flag enabled in Development but disabled in Production.
Default environments
Depending on your plan, projects are created with default environments:
- Free plan: 1 environment (Default)
- Premium plan: Development, Staging, Production
- Enterprise: Unlimited custom environments
MCP Server
Connect AI assistants like Claude, Cursor, and Windsurf directly to FlagDash. Manage flags, configs, and AI configs through natural language — no dashboard needed.
What is MCP?
The Model Context Protocol lets AI assistants call tools on external services. FlagDash exposes 27 tools and 4 resources so your AI can create flags, toggle rollouts, update configs, and more — all through conversation.
Endpoint
FlagDash exposes a single MCP endpoint over HTTP:
https://flagdash.io/mcp
Uses the StreamableHTTP transport. Compatible with Claude Code, Cursor, Windsurf, Claude Desktop, and any MCP client that supports HTTP.
Authentication
FlagDash MCP uses OAuth 2.1 for authentication. When you add FlagDash to an MCP-compatible client (Claude Code, Cursor, etc.), the client handles the OAuth flow automatically — your browser opens a consent page, you approve, and you're connected.
How it works
-
The MCP client registers itself with FlagDash (automatic, via
POST /oauth/register) - It redirects you to FlagDash's authorization page in your browser
- You log in (if needed) and approve the requested permissions
- The client receives an access token and can call MCP tools on your behalf
OAuth Discovery
MCP clients discover the OAuth configuration automatically via well-known endpoints:
GET https://flagdash.io/.well-known/oauth-protected-resource
GET https://flagdash.io/.well-known/oauth-authorization-server
Scopes
| Scope | Description |
|---|---|
| mcp:read | Read flags, configs, AI configs, projects, environments |
| mcp:write | Create, update, delete flags, configs, AI configs |
| mcp:admin | Full access (implies read + write) |
Default consent grants mcp:read mcp:write.
Access tokens expire after 1 hour and are automatically refreshed.
Important:
The MCP endpoint only accepts OAuth 2.1 tokens (mcp_at_ prefix).
Management API keys, session tokens, and SDK keys are rejected.
Use the REST Management API for server-to-server automation.
Claude Code
One command — run this in your terminal:
# Adds FlagDash MCP — opens browser for OAuth consent on first use
claude mcp add -t http flagdash "https://flagdash.io/mcp"
Or add manually to .mcp.json in your project root:
{
"mcpServers": {
"flagdash": {
"type": "http",
"url": "https://flagdash.io/mcp"
}
}
}
Claude Desktop
Go to Settings → Connectors → Create and enter the endpoint URL,
or add manually to claude_desktop_config.json:
{
"mcpServers": {
"flagdash": {
"url": "https://flagdash.io/mcp"
}
}
}
Cursor
Open Settings → MCP Tools → New MCP Server,
or add manually to .cursor/mcp.json in your project root:
{
"mcpServers": {
"flagdash": {
"type": "http",
"url": "https://flagdash.io/mcp"
}
}
}
Windsurf
Open Cascade → MCP → View raw config,
or edit ~/.codeium/windsurf/mcp_config.json directly:
{
"mcpServers": {
"flagdash": {
"serverUrl": "https://flagdash.io/mcp"
}
}
}
ChatGPT
ChatGPT supports MCP via Developer Mode (Pro and Plus plans). The endpoint must be reachable over HTTPS.
https://flagdash.io/mcp
Authorization: Bearer pat_your_key_here
Quick Test
Verify the MCP endpoint is working:
# Initialize MCP session (requires an OAuth access token)
curl -X POST https://flagdash.io/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer mcp_at_your_oauth_token" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": { "name": "test", "version": "0.1" }
}
}'
# List available tools
curl -X POST https://flagdash.io/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer mcp_at_your_oauth_token" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
Tools
27 tools organized by domain. Your AI assistant discovers these automatically.
Feature Flags
| Tool | Description |
|---|---|
| ListFlags | List all flags in a project |
| GetFlag | Get a flag with all environment states |
| CreateFlag | Create a new feature flag |
| UpdateFlag | Update flag name, description, or tags |
| DeleteFlag | Delete a feature flag |
| ToggleFlag | Enable or disable a flag in an environment |
| SetFlagRollout | Set percentage rollout (0-100) |
| SetFlagRules | Set targeting rules for a flag |
| EvaluateFlag | Evaluate a single flag for a user context |
| EvaluateAllFlags | Evaluate all flags for a user context |
Remote Configs
| Tool | Description |
|---|---|
| ListConfigs | List all configs in a project |
| GetConfig | Get a config with environment values |
| CreateConfig | Create a new remote config |
| UpdateConfig | Update config metadata |
| DeleteConfig | Delete a remote config |
| ToggleConfig | Activate or deactivate in an environment |
| SetConfigValue | Set config value for an environment |
AI Configs
| Tool | Description |
|---|---|
| UseAiConfigs | Load all AI config files into the LLM context — just say "use ai configs" |
| ListAiConfigs | List AI config files for an environment |
| GetAiConfig | Get a specific AI config file |
| CreateAiConfig | Create a new skill, rule, or agent file |
| UpdateAiConfig | Update content or move to a folder |
| DeleteAiConfig | Delete an AI config file |
| InitializeAiConfigs | Bootstrap default files for an environment |
Projects & Environments
| Tool | Description |
|---|---|
| ListProjects | List all projects for the account |
| GetProject | Get project details |
| ListEnvironments | List environments for a project |
Resources
MCP resources provide read-only snapshots. AI clients can read these to load context before performing actions.
| URI | Description |
|---|---|
| flagdash://ai-configs | All AI config files across projects & environments |
| flagdash://flags | All feature flags |
| flagdash://configs | All remote configs |
| flagdash://projects | All projects and environments |
Example Prompts
Once connected, ask your AI assistant things like:
"List all my projects and their environments"
"Create a boolean flag called dark-mode"
"Enable new-checkout in production with 25% rollout"
"Evaluate all flags for user_id=alice, plan=premium"
"Create an AI config skill called code-review.md"
"What flags are enabled in staging right now?"
Load AI Configs into LLM Context
AI config files (agent, skills, rules) define how your AI should behave.
With the UseAiConfigs tool, you can load them directly into the
LLM’s context with a single sentence — no code required.
How it works
1. Create your AI config files
in the dashboard or via MCP — write an agent.md
with your AI’s persona, add rule
files for guardrails,
and skill
files for capabilities.
2. Say "use ai configs"
in any MCP conversation. The LLM calls UseAiConfigs,
which fetches all your files and returns them as instructions
the LLM must follow.
3. The LLM follows your rules. Your agent persona, skills, and guardrails are now active. Update them in the dashboard — changes take effect on the next load.
"Use ai configs, then review this PR"
"Load ai configs and help me debug this error"
"Apply ai configs, then summarize our API changes"
"Use ai configs" (just that — it works)
Troubleshooting
"Invalid or missing authentication"
The MCP endpoint requires OAuth 2.1 tokens (mcp_at_ prefix).
MCP clients handle OAuth automatically. For REST API access, use a
pat_
or sk_
key instead.
Tools not appearing in your AI assistant
Reload your editor window after saving the MCP config file. Verify the endpoint is reachable by running the curl test above.
Connection timeout
Make sure the URL in your MCP config matches
https://flagdash.io/mcp
and your network can reach it.
SSE / Realtime
Get instant updates when flags, configs, or AI configs change using Server-Sent Events (SSE). Available on client-side SDKs (JavaScript, React).
How it works
When you enable realtime: true in the SDK,
it opens an SSE connection to the server. When any flag, config, or AI config
changes in your environment, the server pushes an event and the SDK automatically
refreshes its local cache. Your UI re-renders with the new values instantly.
SSE Endpoint
/api/v1/sse?api_key=YOUR_KEY
SSE stream for real-time updates (requires API key with read scope)
The SSE endpoint uses the api_key query parameter
for authentication (since EventSource cannot set custom headers). The SDK handles
this automatically.
Event Types
| Event | Description |
|---|---|
| connected | SSE connection established |
| flag_updated | A flag was toggled, updated, or had its rollout changed |
| config_updated | A remote config value was changed |
| ai_config_updated | An AI config file was created, updated, or deleted |
| ping | Keep-alive signal (every 20 seconds) |
JavaScript SDK
import { FlagDash } from '@flagdash/sdk';
const client = FlagDash.init({
sdkKey: 'sk_...',
realtime: true, // Enable SSE
});
// Flags, configs, and AI configs auto-refresh on changes
client.on('flags_updated', (flags) => {
console.log('Flags changed:', flags);
});
client.on('configs_updated', (configs) => {
console.log('Configs changed:', configs);
});
// Toggle SSE at runtime
client.disableRealtime();
client.enableRealtime();
React SDK
import { FlagDashProvider, useFlag } from '@flagdash/react';
// Enable SSE in the provider — all hooks auto-update
function App() {
return (
<FlagDashProvider sdkKey="sk_..." realtime>
<FeatureToggle />
</FlagDashProvider>
);
}
function FeatureToggle() {
// Automatically re-renders when the flag changes via SSE
const enabled = useFlag('new-checkout', false);
return enabled ? <NewCheckout /> : <OldCheckout />;
}
Polling Fallback
If SSE is not suitable for your environment, you can use polling instead.
Set refreshInterval to a millisecond interval
and the SDK will periodically refetch all data.
const client = FlagDash.init({
sdkKey: 'sk_...',
refreshInterval: 30000, // Poll every 30 seconds
});
Server SDKs
Server-side SDKs (Node.js, Go, Python) use TTL-based caching instead of SSE. For server-side real-time updates, use webhooks to receive HTTP callbacks when resources change.
Webhooks
Get notified when flags or configs change. Webhooks are available on Premium and Enterprise plans.
Events
| Event | Description |
|---|---|
| flag.created | A new feature flag was created |
| flag.updated | A flag was modified (rules, rollout, settings) |
| flag.toggled | A flag was enabled or disabled in an environment |
| flag.deleted | A feature flag was deleted |
| config.created | A new remote config was created |
| config.updated | A remote config value or settings were changed |
| config.toggled | A remote config was activated or deactivated in an environment |
| config.deleted | A remote config was deleted |
Payload
{
"id": "whevt_a1b2c3d4e5f6g7h8",
"type": "flag.toggled",
"timestamp": "2026-02-14T10:30:00Z",
"project_id": "prj_abc123",
"environment_id": "env_prod456",
"data": {
"flag": {
"id": "flag_xyz789",
"key": "new-checkout",
"name": "New Checkout Flow",
"description": "Enables the redesigned checkout",
"flag_type": "boolean",
"default_value": null,
"tags": [],
"is_archived": false,
"project_id": "prj_abc123"
},
"changes": {
"enabled": { "from": false, "to": true }
}
}
}
Management API
Full CRUD operations for flags and configs. Requires a pat_
Personal Access Token or an API key (sk_) with write scopes.
Feature Flags
/api/v1/manage/flags
List all flags
/api/v1/manage/flags
Create a new flag
/api/v1/manage/flags/:key
Update a flag
/api/v1/manage/flags/:key
Delete a flag
/api/v1/manage/flags/:key/toggle
Toggle flag in an environment
/api/v1/manage/flags/:key/rules
Update targeting rules
/api/v1/manage/flags/:key/rollout
Update rollout percentage
Remote Configs
/api/v1/manage/configs
List all configs
/api/v1/manage/configs
Create a new config
/api/v1/manage/configs/:key
Update a config
/api/v1/manage/configs/:key
Delete a config
/api/v1/manage/configs/:key/value
Update config value for an environment