Feature Flags
Ship code behind flags and control who sees it — without redeploying. Manage flags from your Simplr dashboard, deliver them through the SDK, and evaluate them locally and deterministically so there's no network call on every check. Roll out by percentage, target specific users, and let your CI journey tests gate the rollout automatically.
Overview
Feature Flags provide:
- SDK-delivered config — the JS and Flutter SDKs fetch flags with a public key and cache them
- Local, deterministic evaluation —
isEnabled()runs in-process; the same user always gets the same answer - Percentage rollouts & targeting — release to X% of users, hand-pick user IDs, or match attributes with rules
- Production & sandbox — flags live in a
liveortestenvironment, keyed independently - Test-gated progressive rollout — link a flag to a CI journey tag; passing tests advance the rollout, a failure halts it and alerts you
- Audit log & rollback — every change is recorded with who, what, and the from/to percentage
- Per-request billing — 1,000,000 flag config requests per month free, then $0.03 per 1,000
Because evaluation happens locally, flag checks are instant and free — you're only billed when an SDK fetches the config (on init and refresh), not per isEnabled() call.
How rollout percentages work
A percentage rollout needs no knowledge of your total user count. Each SDK hashes a stable identifier (the user ID, or the device ID for anonymous users) together with the flag key and maps it to a bucket from 0–99:
bucket = hash(`${flagKey}:${userId}`) % 100
enabled = bucket < rollout_percentage
Because the hash is deterministic, a given user lands in the same bucket every time — so they don't flicker between on and off across sessions, and raising the percentage only ever adds users (never removes the ones already in). This is what makes blue-green and canary releases safe.
Evaluation order for isEnabled():
- If the flag is disabled, return
false. - If the user is in
target_user_ids, returntrue. - If any rule matches the supplied attributes, return
true. - Otherwise fall back to the percentage bucket.
Quick start
1. Create a flag
In the dashboard, go to Developer → Feature Flags → New flag. Give it a key (e.g. new-checkout), choose Production or Sandbox, and set a starting rollout percentage. You can also add targeted user IDs and a description.
2. Get a public key
Flags are read with a public key (pk_live_* / pk_test_*) — never a secret key. Create one under Developer → API Keys. Public keys can only read flag config; they can't touch any other API.
3. Install the SDK and evaluate
JavaScript
import { simplrFlags } from "@simplr-ai/js";
await simplrFlags.initialize({
apiKey: "pk_live_xxx",
environment: "live", // or "test"
refreshIntervalMs: 60000, // optional; 0 disables auto-refresh
});
simplrFlags.setUser("user_123"); // optional; falls back to device ID
if (simplrFlags.isEnabled("new-checkout")) {
// show the new checkout
}
// With per-call context (rules / overrides):
simplrFlags.isEnabled("new-checkout", {
userId: "user_123",
attributes: { plan: "growth", country: "GB" },
});
Flutter
import 'package:simplr_fraud/simplr_fraud.dart';
final flags = SimplrFlags();
await flags.initialize(
apiKey: 'pk_live_xxx',
environment: 'live', // or 'test'
refreshInterval: const Duration(minutes: 1),
);
flags.setUser('user_123'); // optional; falls back to device ID
if (flags.isEnabled('new-checkout')) {
// show the new checkout
}
// With per-call context (rules / overrides):
flags.isEnabled(
'new-checkout',
userId: 'user_123',
attributes: {'plan': 'growth', 'country': 'GB'},
);
Both SDKs fetch the config once on initialize() and again on each refresh, keeping the last-known config if a refresh fails — so a network blip never flips your flags off.
Reading flags directly (HTTP)
If you're not using an SDK, fetch the config yourself with a public key:
curl "https://api.simplr-ai.com/v1/flags?environment=live" \
-H "X-API-Key: pk_live_xxx"
{
"content": {
"environment": "live",
"flags": [
{
"key": "new-checkout",
"enabled": true,
"rollout_percentage": 40,
"target_user_ids": ["user_123"],
"rules": [{ "attribute": "plan", "op": "eq", "value": "growth" }]
}
]
}
}
Each call to this endpoint counts as one billable flag request (see Pricing). Evaluate locally with the bucket formula above — don't call this per check.
Targeting & rules
- Targeted users — add user IDs to a flag to always-on it for specific accounts (beta testers, internal staff), regardless of the percentage.
- Rules — match on attributes you pass to
isEnabled(). Each rule is{ attribute, op, value }withopofeq,neq, orcontains. Any matching rule turns the flag on.
// Flag rule: { attribute: "plan", op: "eq", value: "growth" }
simplrFlags.isEnabled("new-checkout", { attributes: { plan: "growth" } }); // → true
Test-gated progressive rollout
Tie a flag's rollout to your CI journey tests so it only reaches more users while your tests are green.
- Tag a journey with a rollout tag (e.g.
ci) in the journey Settings. - On the flag, set Rollout mode to Auto and choose:
- Gating tag — the journey tag to watch (e.g.
ci) - Step % — how much to advance each interval (e.g. 10)
- Max % — the ceiling to stop at (e.g. 100)
- Interval — how often to consider advancing (minutes)
- Gating tag — the journey tag to watch (e.g.
A controller runs every few minutes. For each auto flag:
- All gating journeys passing + interval elapsed + below max → advance
rollout_percentageby the step (recorded in the audit log, sourcecontroller). - Any gating journey failing → halt at the current percentage and send an alert to your org's billing email. The percentage is never lowered automatically.
This gives you automated progressive delivery: a code change runs your CI journeys, and only green tests let the feature reach more real users.
Run journey tests in your CI
Add this step to your own pipeline. It runs every journey tagged ci and fails the build if any of them fail — no extra script or dependency to install. The rollout controller then watches those same runs to gate the flag.
First add a CI secret SIMPLR_API_KEY (a secret sk_* key from Developer → API Keys), then:
# .github/workflows/simplr-journeys.yml
name: Simplr journey tests
on: [push, pull_request]
jobs:
journeys:
runs-on: ubuntu-latest
steps:
- name: Run Simplr journeys (fail build on any failure)
run: |
R=$(curl -sf -X POST "$SIMPLR_API_URL/v1/journeys/ci/run" \
-H "X-API-Key: $SIMPLR_API_KEY" -H "Content-Type: application/json" \
-d '{"tag":"ci"}')
echo "$R" | jq .
test "$(echo "$R" | jq '.content.failed')" -eq 0
env:
SIMPLR_API_KEY: ${{ secrets.SIMPLR_API_KEY }}
SIMPLR_API_URL: https://api.simplr-ai.com
The same pattern works in any CI (GitLab, CircleCI, Jenkins) — it's just a curl plus a check that failed == 0. Use the same tag here and in the flag's Gating tag so the rollout advances only when these tests are green.
Audit log & rollback
Every create, toggle, percentage change, halt, resume, and rollback is written to the flag's history with the actor, the from/to percentage, a reason, and whether the change was manual or from the controller. Open the Logs (📜) action on any flag to browse the full audit trail (paginated). Use Rollback (↺) to return a flag to a safe state in one click.
Environments
Each flag belongs to either the live (Production) or test (Sandbox) environment and is keyed independently, so you can validate a flag in sandbox with a pk_test_* key and promote the same key to production with a pk_live_* key. The SDK loads one environment per instance, chosen by the environment option (defaulting to the key's own environment).
Pricing
Feature Flags use shared credits per config request — 1,000,000 live requests per month free, then 6 credits per 1,000. Test-key requests and local isEnabled() checks are never billed. See Pricing → Feature Flags for details and how to tune your refresh interval.