Entitlements vs Feature Flags — and why your SaaS needs both
They look similar. They both gate what users can do. But confusing the two is the single most common reason B2B SaaS teams end up with `if (plan === 'enterprise')` scattered across their codebase.
April 22, 2026 • 10 min read • Alexandre Bergère


Entitlements vs Feature Flags — and why your SaaS needs both
You ship a new feature behind a flag. A PM asks you to restrict it to Pro users. So you add a targeting rule: plan == "pro". It works. You move on.
Three months later, the pricing changes. The feature moves from Pro to a new Growth plan. Someone updates Stripe. Someone else updates the flag. But not at the same time. For two days, free users see a Pro feature. Nobody notices until a customer emails support asking why they lost access to something they never paid for.
This isn't a hypothetical. It happens every week in SaaS teams that treat feature flags as the place to encode business logic. And the root cause is always the same: two tools were available — feature flags and entitlements — and only one of them was used.
The good news: once you understand which tool answers which question, the problem disappears. This article draws a clear line between the two, explains why confusing them is expensive, and shows how they work together.
The 30-second definition
A feature flag answers a technical question: should this code path execute for this user, right now, in this environment?
Flags are about the state of the code. They're owned by engineers and product managers. They're typically short-lived — they exist to gate a rollout, enable a beta, or stand by as a kill-switch. Most flags should disappear within a few weeks of going to 100%.
An entitlement answers a commercial question: does this customer have the contractual right to use this capability, and up to what limit?
Entitlements are about the state of the contract. They're owned by product and revenue teams. They're long-lived — they exist as long as the plan exists. A new enterprise tier might introduce dozens of new entitlements, and they'll live alongside the product for years.
The four dimensions that separate them
The confusion is understandable because both can be expressed as booleans and both gate behavior. But they differ on four critical dimensions:
| Dimension | Feature Flag | Entitlement |
|---|---|---|
| Question answered | "Should this code run?" | "Is the customer allowed to do this?" |
| Owner | Engineering, Product | Product, Revenue, Customer Success |
| Lifespan | Short (rollout → cleanup) | Long (tied to the commercial offer) |
| Source of truth | The deployed code | The signed contract |
| Typical values | Boolean, string variant, JSON config | Boolean, numeric quota, JSON config |
| Changes triggered by | Deploys, experiments, rollouts | Plan upgrades, contracts, usage |
Notice the last row. Flags change when your engineering team ships. Entitlements change when your revenue team closes a deal. Those two rhythms are completely different — and that's why trying to run both through the same tool usually fails.
When to use which: three concrete scenarios
Scenario 1 — Shipping a new AI assistant
You're building a new AI Assistant feature. You want to roll it out to 10% of users first, validate it works, then ramp to 100%.
This is a feature flag problem. Create a flag ai-assistant-enabled, put it behind a percentage rollout targeting rule, and monitor error rates. Once you're at 100% and confident, either remove the flag or keep it as a kill-switch.
const showAIAssistant = await client.getBooleanValue(
'ai-assistant-enabled',
false,
{ instanceId: 'inst_acme' }
);There's no commercial dimension here yet. It's purely about code readiness.
Scenario 2 — Monetizing the AI assistant
Three months later, the feature is stable, everyone loves it, and Sales wants to charge for it. You decide AI Assistant is included in the Pro and Enterprise plans, with a monthly token quota: 100K tokens for Pro, 1M for Enterprise.
This is an entitlement problem — on two fronts.
First, a boolean entitlement ai-assistant: does this customer have access at all?
Second, a numeric entitlement ai-tokens-per-month: what's their monthly quota?
// Before letting the user call the AI
const hasAccess = await client.checkEntitlement('ai-assistant');
if (!hasAccess.granted) {
return showUpgradePrompt();
}
// When processing the request
const usage = await client.meterUsage('ai-tokens-per-month', {
model: 'gpt-4o',
tokens_in: 1200,
tokens_out: 450
});
if (!usage.within_limit) {
return showQuotaExceeded(usage.limit, usage.current);
}The code is identical across Starter, Pro, and Enterprise customers. The contract decides what happens, not the code.
Scenario 3 — The hybrid case: early access for select customers
Now Sales negotiates a custom deal. Acme wants early access to a new "Voice Interface" feature that's still in beta.
You need both tools.
- The feature flag
voice-interface-enabledgates the code. It's still in technical beta — you want to be able to roll it back instantly if something breaks. - An entitlement on Acme's license grants them the right. Or — if you're using Kaiten — a
FLAG_GRANTvoucher that temporarily overrides the flag for that specific customer's instance.
This is the scenario where most flag-only or entitlement-only tools break down. You need the two systems to talk to each other.
Why most B2B SaaS end up with if (plan === 'enterprise') everywhere
Three reasons. All three are fixable.
Reason 1 — The tools are usually separate. Your flag tool (LaunchDarkly, Unleash, Flagsmith) doesn't know what the customer paid for. Your billing tool (Stripe, Lago) doesn't know which features are live in prod. So engineers take the shortest path: hardcode the plan check. It works for three customers. It collapses at thirty.
Reason 2 — Engineering and Revenue teams don't share a data model. Engineering thinks in terms of flags, variants, rollout rules. Revenue thinks in terms of plans, line items, quotas. Without a shared abstraction, every new commercial package becomes a refactor.
Reason 3 — There's no open standard for entitlements. Feature flags have OpenFeature and OFREP (the OpenFeature Remote Evaluation Protocol) as industry-backed standards. Entitlements have… nothing. Each vendor invents their own SDK, their own API shape, their own lock-in story.
How Kaiten thinks about it
Kaiten was built on a single premise: flags and entitlements belong in the same control plane, because they describe two sides of the same coin — what's running, and what the customer paid for.
Concretely:
Feature flags in Kaiten are OpenFeature-native and OFREP-compatible.
You use the standard OpenFeature SDKs — Go, TypeScript, Python, Java, Ruby, and more — and Kaiten's engine exposes the OFREP /ofrep/v1/evaluate/flags/{key} endpoint out of the box. No proprietary SDK. No lock-in. If you decide to move off Kaiten tomorrow, your flag code doesn't change — just the backend behind OFREP. CEL (Common Expression Language) is used for targeting rules, so your conditions are portable too.
Entitlements in Kaiten are first-class citizens, not bolted onto the flag engine.
Three types — Boolean, Numeric (with metering), JSON Config — and they live in the License that represents a customer's plan. When Sales closes a deal, they assign the License. The entitlements follow automatically. When a customer upgrades, a single License change cascades to every Instance the customer has deployed. Zero PRs. Zero deploys. Zero if (plan === 'enterprise').
They share one audit trail. Every flag evaluation and every entitlement check writes to the same audit trail. Compliance gets one source of truth. Product gets adoption metrics across both. Sales gets consumption data. All without a single line of instrumentation code.
And they can reference each other.
A voucher can grant a feature flag (FLAG_GRANT voucher type — unique to Kaiten). A feature flag can read from an entitlement as part of its targeting rule. The two systems are aware of each other because they live in the same model.
Here's what that looks like in practice. You define a feature as an entitlement on your Pro plan:
plan:
key: pro
entitlements:
- feature: advanced-analytics
value: true
- feature: api-calls-per-month
value: 10000
type: NUMBER
reset_period: MONTHLYThen you progressively roll out a new version of the UI with a flag that references the entitlement:
flag:
key: advanced-analytics-v2-ui
targeting:
- constraint: entitlement("advanced-analytics") == true
rollout:
percentage: 25The flag only evaluates for users who have the entitlement. A free user will never see it, regardless of the rollout percentage. A Pro user has a 25% chance of seeing the new UI. If that Pro user downgrades to Free, their entitlement is revoked by the billing event, and the flag stops matching — automatically. No manual sync. No stale targeting rules.
A practical decision tree
Next time you're about to add a gate to your code, ask yourself three questions:
-
Will this still exist in six months? No → feature flag (probably a short-lived one). Yes → likely an entitlement.
-
Does the answer depend on what the customer paid? Yes → entitlement. No → feature flag.
-
Who decides when it turns on? Engineering / Product → feature flag. Sales / Customer Success / the customer themselves → entitlement.
If the answer puts you on the fence, it's probably the hybrid case — and that's exactly why you need both tools in the same control plane.
The short version
- Feature flags = is this code ready to run?
- Entitlements = is this customer allowed to run it, and up to what limit?
- You need both.
- They work better together than apart.
- Use open standards (OpenFeature, OFREP) for flags. Demand the same for entitlements.
Kaiten is the open-source control plane that unifies both, with OpenFeature-native flags, first-class entitlements, and one audit trail across the two. The contract enforces itself — not the other way around.
Unify flags and entitlements in one control plane
Kaiten connects OpenFeature-native flags, first-class entitlements, and metering in one model.