Reference
Feature flags
Feature access in OpsDash is controlled by two systems that work together: Stripe Entitlements (what the plan includes) and the feature_flags table (per-org toggles with role restrictions).
How feature access works
Every protected feature passes through four layers:
- Entitlement check: Is the feature included in the org's Stripe plan?
- Subscription status: Must be
activeortrialing - Feature flag:
feature_flags.enabled— is it enabled for this org? - Role check: Is the user's role in
feature_flags.allowed_roles?
The feature_flags table
One row per feature per org. Seeded from defaultFeatures on org creation.
feature_flags (
id uuid PK,
org_id uuid FK → organizations,
feature_key text, -- e.g. 'crm:contacts'
enabled boolean, -- on/off for this org
allowed_roles text[], -- NULL = all roles; array = restrict
created_at timestamptz
)UI: Settings → Admin → Feature Flags (owner/admin only)
Server-side functions
| Function | Location | Purpose |
|---|---|---|
| canAccessFeature() | org-context.tsx | Client: sidebar, command palette, route guards |
| requireFeatureAccess() | admin.ts → admin-feature-gates.ts | Server actions: returns error if no access |
| ensureFeatureAccess() | admin.ts → admin-feature-gates.ts | Layouts: redirects to dashboard if no access |
| ensureTieredFeatureAccess() | admin.ts → admin-feature-gates.ts | Layouts: Pro/Enterprise sub-features (e.g. crm:export layout) |
| checkFeature() | admin.ts → admin-feature-gates.ts | Subscription/entitlement check only — no role check |
| checkUsageLimit() | helpers.ts | Usage-capped features — checks count vs. plan limit |
All feature keys
CRM
| Key | Free | Pro | Enterprise |
|---|---|---|---|
| crm:contacts | 250 | 10,000 | Unlimited |
| crm:companies | 50 | 2,000 | Unlimited |
| crm:deals | 50 | 2,000 | Unlimited |
| crm:leads | 100 | 5,000 | Unlimited |
| crm:lead-scoring | ✓ | ✓ | ✓ |
| crm:deal-stages | ✓ | ✓ | ✓ |
| crm:activities | ✓ | ✓ | ✓ |
| crm:contact-fields | ✓ | ✓ | ✓ |
| crm:gdpr-export | ✓ | ✓ | ✓ |
| crm:quotes | — | ✓ | ✓ |
| crm:csv-import | — | ✓ | ✓ |
| crm:contact-filters | — | ✓ | ✓ |
| crm:lead-filters | — | ✓ | ✓ |
| crm:data-cleanup | — | ✓ | ✓ |
| crm:export | — | ✓ | ✓ |
| crm:email-integration | — | ✓ | ✓ |
| crm:custom-fields | — | ✓ | ✓ |
workspace_data_export is a Pro/Enterprise org flag (not in defaultFeatures) stored in feature_flags; it gates crm:export (CRM → Data Export) together with the crm module. It appears in Workspace admin → Feature flags.
Projects
| Key | Free | Pro | Enterprise |
|---|---|---|---|
| projects:crud | 3 | 50 | Unlimited |
| projects:tasks | 100 | Unlimited | Unlimited |
| projects:kanban | ✓ | ✓ | ✓ |
| projects:calendar | ✓ | ✓ | ✓ |
| projects:comments | ✓ | ✓ | ✓ |
| projects:time-tracking-view | ✓ | ✓ | ✓ |
| projects:subtasks | — | ✓ | ✓ |
| projects:milestones | — | ✓ | ✓ |
| projects:gantt | — | ✓ | ✓ |
| projects:dependencies | — | ✓ | ✓ |
| projects:time-tracking-log | — | ✓ | ✓ |
| projects:estimation | — | ✓ | ✓ |
| projects:budget | — | ✓ | ✓ |
| projects:team-management | — | ✓ | ✓ |
| projects:custom-fields | — | ✓ | ✓ |
Platform
| Key | Free | Pro | Enterprise |
|---|---|---|---|
| platform:audit-logs | — | — | ✓ |
| platform:rbac-viewer | — | — | ✓ |
| platform:custom-branding | — | — | ✓ |
| platform:sso | — | — | ✓ |
| platform:api-access | — | — | ✓ |
| platform:webhooks | — | ✓ | ✓ |
| platform:reports | — | ✓ | ✓ |
| platform:lead-capture-api | — | ✓ | ✓ |
| platform:onboarding | ✓ | ✓ | ✓ |
Feature key naming convention
type FeatureKey =
| `crm:${string}`
| `projects:${string}`
| `billing:${string}`
| `automation:${string}`
| `platform:${string}`
| `forms:${string}`
| `dashboard:${string}`Defined in packages/config/src/types.ts. All keys are used in packages/config/src/defaults.ts (defaultFeatures) and seeded into feature_flags on org creation.