Reference
Feature flags
Feature access in OpsDash is controlled by two systems that work together: plates (the entitlement ladder — free → studio → sales → growth → full_loop → agency, resolved from subscriptions.plan, optionally extended by add-on packs) and the feature_flags table (per-org toggles with role restrictions). Each feature declares a single minPlate — the lowest plate that unlocks it — in packages/config/src/defaults.ts.
How feature access works
Every protected feature passes through four layers:
- Plate check: Is the org's plate at or above the feature's
minPlate(plus any required add-on pack)? - 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: plate-gated 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 |
The plate ladder
Six plates, strictly nested — every plate includes everything below it. Public self-serve pricing shows four columns (Sales is a dormant internal rung; Agency is a “Contact us” lane):
- Free — $0
- Studio — $24/mo
- Sales — $39/mo (dormant; not publicly marketed)
- Growth — $59/mo
- Full Loop — $99/mo
- Agency — $179/mo, sold as “Contact us”
Three flat-rate add-on packs extend a plate without changing it: AI Pack ($25/mo, requires Studio+), Advanced Analytics ($19/mo, Full Loop+), and e-Invoicing / Peppol ($29/mo, Growth+). Keys marked PLANNED below are reserved (wired for gating) but not yet shipped.
All feature keys
CRM
| Key | Min plate | Notes |
|---|---|---|
| crm:contacts | Free | Usage-capped (250 on Free → unlimited on Agency). |
| crm:companies | Free | Usage-capped (50 on Free → unlimited on Agency). |
| crm:activities | Free | Activity log. |
| crm:contact-fields | Free | Standard contact fields. |
| crm:gdpr-export | Free | Per-contact data export. |
| crm:deals | Sales | Usage-capped pipeline deals. |
| crm:leads | Sales | Usage-capped leads. |
| crm:support | Sales | Support tickets (usage-capped). |
| crm:lead-scoring | Sales | |
| crm:deal-stages | Sales | |
| crm:quotes | Sales | |
| crm:csv-import | Sales | |
| crm:custom-fields | Sales | Usage-capped. |
| crm:contact-filters | Sales | |
| crm:lead-filters | Sales | |
| crm:data-cleanup | Sales | Data Health (dedup, bulk-fix). |
| crm:export | Sales | Whole-workspace Data Export (admin-only). |
| crm:email-integration | Sales | |
| crm:ai-lead-enrichment | Studio + AI Pack | PLANNED (reserved AI key). |
| crm:ai-deal-summarizer | Studio + AI Pack | PLANNED (reserved AI key). |
| crm:ai-next-best-action | Studio + AI Pack | PLANNED (reserved AI key). |
workspace_data_export is an 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 | Min plate | Notes |
|---|---|---|
| projects:crud | Free | Usage-capped (3 projects on Free). |
| projects:tasks | Free | Usage-capped tasks. |
| projects:kanban | Growth | |
| projects:calendar | Growth | |
| projects:comments | Growth | |
| projects:time-tracking-view | Growth | |
| projects:subtasks | Growth | |
| projects:milestones | Growth | |
| projects:gantt | Growth | |
| projects:dependencies | Growth | |
| projects:time-tracking-log | Growth | |
| projects:estimation | Growth | |
| projects:budget | Growth | |
| projects:team-management | Growth | |
| projects:custom-fields | Growth | Usage-capped. |
| projects:ai-task-generator | Studio + AI Pack | PLANNED (reserved AI key). |
| projects:ai-standup-summary | Studio + AI Pack | PLANNED (reserved AI key). |
Platform
| Key | Min plate | Notes |
|---|---|---|
| platform:onboarding | Free | |
| platform:feature-flags | Free | This admin surface. |
| platform:rbac-basic | Free | |
| platform:rbac-viewer | Free | Read-only viewer role. |
| platform:notifications-email | Studio | Metered against the email cap. |
| platform:lead-capture-api | Sales | Public lead-capture endpoint. |
| platform:api-access | Full Loop | |
| platform:webhooks | Full Loop | Outbound webhooks. |
| platform:audit-logs | Full Loop | |
| platform:custom-branding | Agency | Always on for white-label distribution. |
| platform:rbac-custom-roles | Agency | PLANNED (reserved). |
| platform:audit-logs-export | Agency | PLANNED (reserved). |
| platform:sso-saml | Agency | PLANNED (reserved). |
| platform:scim-provisioning | Agency | PLANNED (reserved). |
Reserved keys (other modules)
| Key | Min plate | Notes |
|---|---|---|
| dashboard:ai-copilot | Studio + AI Pack | PLANNED (reserved AI key). |
| automation:ai-triage | Studio + AI Pack | PLANNED (reserved AI key). |
| forms:ai-form-builder | Studio + AI Pack | PLANNED (reserved AI key). |
| reports:ai-narrative | Studio + AI Pack | PLANNED (reserved AI key). |
| reports:scheduled | Full Loop | PLANNED (reserved; not in default seed). |
Feature key naming convention
type FeatureKey =
| `crm:${string}`
| `projects:${string}`
| `forms:${string}`
| `dashboard:${string}`
| `analytics:${string}`
| `reports:${string}`
| `automation:${string}`
| `invoicing:${string}`
| `platform:${string}`
| `ap:${string}`
| `contracts:${string}`
| `resource-planning:${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.