Pricing Implementation Plan
Purpose
This plan turns docs/app/product/pricing-strategy.md into an implementation sequence for the app's own SaaS subscription model.
This plan is specifically for:
agency -> aegi SaaS subscriptionorg/settings/billing- Polar-backed SaaS billing and entitlement enforcement
This plan is not for:
agency -> its clientsbilling through Stripe Connectorg/(dashboard)/billing- portal invoice/subscription payment flows for client accounts
That split already exists in the repo and must stay explicit.
Current Repo Reality
The current app already has a working SaaS billing baseline for organization subscriptions:
- Polar is the active provider for org SaaS billing.
- The app billing surface lives in
apps/app/src/app/[locale]/org/(dashboard)/settings/billing/page.tsx. - Backend SaaS billing state lives under
packages/backend/convex/org/billing.tsandpackages/backend/convex/org/billing/*. - Plan definitions live in
packages/backend/convex/lib/billingPlans.ts. - Subscription vocabulary and billing-state logic live in
packages/backend/convex/lib/subscriptions.ts. - Shared entitlement baselines live in
packages/capabilities/src/entitlements.ts. - Usage reporting already exposes plan state and entitlement visibility through
packages/backend/convex/org/premium.tsandpackages/backend/convex/org/premium/summary.ts.
Core Mismatch To Resolve First
The current starter billing model does not match the pricing strategy yet.
Current code assumes:
- internal
basicmeans the default non-paid baseline - paid tiers are
pro,business, andenterprise - trial defaults are centered on
pro - entitlement baselines assume the current starter tier ladder
The pricing strategy says:
- there is no free plan
- paid plans are
Basic,Pro, andBusiness Enterpriseis not a standard self-serve base tier; custom enterprise behavior only appears for storage above+1 TB- plan value is defined by seats, storage, and feature differences
That means this is not just a copy change. It is a pricing-model migration.
Recommended Product Model
1. Keep Polar for SaaS billing
Do not reopen provider choice as part of this work.
The repo already uses Polar for org SaaS billing and Stripe for agency-operated client billing. Keep that split.
2. Separate "no active subscription" from the paid Basic plan
Recommended internal model:
freeorinactive= no paid subscription / fallback account statebasic= paid Basic plan from the pricing docpro= paid Pro planbusiness= paid Business plan
Do not keep using basic to mean both "free baseline" and "paid Basic tier".
This change should be handled as a staged schema/code migration because basic is currently overloaded in validators, entitlement resolution, UI labels, usage summaries, audit logs, tests, and platform summaries.
3. Treat the pricing doc as the single source of truth for commercial packaging
The pricing strategy should define:
- plan names
- monthly and annual pricing
- included seats
- extra seat pricing rules
- included storage
- feature availability
- trial rules
- storage add-ons
- upgrade/downgrade rules
Code should derive from a canonical pricing config instead of scattering these values across UI and business logic.
Canonical Decisions To Lock Before Coding
These need explicit answers before implementation starts. The recommended defaults below match the pricing doc and current repo shape.
A. Provider packaging model
Recommended:
- one Polar product per base plan per billing interval
- separate Polar products or add-on line items for storage packs
- seat overages modeled in app state first, then synced to provider-backed add-on quantities if Polar supports the exact packaging cleanly
B. Seat counting rule
Recommended:
- count active org staff users toward seats
- count pending org staff invites toward reserved seats
- do not count portal users toward staff seats
- Basic blocks additional staff above included seats
- Pro and Business allow extra paid seats
C. Downgrade behavior
Recommended:
- block downgrades if current active staff + pending invites exceed the target tier's included seats plus purchased extra seats
- block downgrades if current storage exceeds target included storage plus purchased storage add-ons
- show exact remediation requirements before the user confirms the downgrade
D. Trial behavior
Recommended:
- new org signup can start a 14-day trial on
basic,pro, orbusiness - upgrade trials remain one-time per tier per organization
- no storage add-ons during trials
- trial storage cap is 2 GB total regardless of trial tier
- when trial storage exceeds 2 GB, block new uploads at the shared upload boundary
E. Support tier handling
Recommended:
- store support level as pricing metadata only for now
- do not build runtime feature gating for support response targets
- expose it in billing UI and internal ops context only
Implementation Workstreams
Workstream 1: Canonical pricing model
Create one canonical pricing module for the app subscription model.
It should define:
- plan ids:
basic,pro,business - internal non-paid state:
freeorinactive - monthly and annual prices
- included seats
- extra seat price rules
- included storage
- storage add-on catalog
- feature matrix
- trial rules
- self-serve availability
Primary targets:
packages/backend/convex/lib/subscriptions.tspackages/backend/convex/lib/billingPlans.tspackages/capabilities/src/entitlements.ts
Result:
- all UI and billing logic read from one product-definition layer
- no hardcoded plan matrix drift between docs, backend, and UI
Workstream 2: Subscription-tier migration
This is the riskiest part and should be done first in the backend.
Required changes
- replace the current overloaded
basicmeaning with a distinct non-paid state - remove standard
enterprisefrom the base self-serve ladder - update validators, TypeScript unions, entitlement mapping, plan comparisons, trial defaults, and status labels
- update any stored records and derived summaries that reference the old tier set
Likely affected areas
packages/backend/convex/lib/subscriptions.tspackages/backend/convex/lib/billingPlans.tspackages/backend/convex/schema.tspackages/backend/convex/org/billing/*packages/backend/convex/platform/subscriptions*packages/backend/convex/platform/directory/organizations.tsapps/app/src/app/[locale]/_lib/convex/api/org/billing.ts- locale files in
apps/app/messages/*.json - billing tests under
packages/backend/tests/org/billing/*
Migration rule
Use a widen-migrate-narrow rollout:
- widen schema and code to understand both old and new tier vocabulary
- migrate existing organization subscription fields and dependent records
- switch UI and provider sync to the new vocabulary
- narrow schema and delete old-tier compatibility paths
Workstream 3: Entitlements and feature gating
Map the pricing doc's feature matrix onto canonical entitlements.
Current gap
Existing entitlements only cover:
- custom domain
- API access
- webhooks
- audit logs
- data export
- seat and portal-member limits
The pricing doc also introduces or sharpens:
- remove aegi branding
- full white-label portal
- multiple internal teams
- SSO
- support level
- storage limits
Recommended approach
Add canonical entitlements for runtime-gated product behavior:
customDomainremoveBrandingfullWhiteLabelPortalapiAccesswebhooksmultipleInternalTeamsssoseatsIncludedstorageIncludedBytes
Do not add support-response-time enforcement as a runtime entitlement unless a real product workflow needs it.
Enforcement targets
- portal branding controls
- custom-domain settings
- API and webhook surfaces
- future internal-team management surfaces
- future SSO setup surfaces
- staff invite / activation flows
- upload URL generation and file attach flows
Workstream 4: Seat model and enforcement
The repo already has entitlement vocabulary for seats, but not a complete commercial seat system.
Required capabilities
- compute included seats from the current plan
- compute purchased extra seats
- compute consumed seats from active staff plus pending invites
- show over/under state in usage and billing UI
- block invite flows when the org exceeds seat allocation
- support add/remove seat adjustments for Pro and Business
- prevent Basic from purchasing seat add-ons
Backend additions
Add explicit organization billing fields or a dedicated add-on record for:
- purchased extra seat count
- effective seat limit snapshot
- last synced provider quantity or add-on linkage
UI additions
Add seat reporting and management to:
org/settings/billingorg/settings/usage- any org staff invite/create surfaces that need preflight enforcement
Workstream 5: Storage metering and add-ons
This is mostly new work and should not be treated as a small follow-up.
Why this is a separate workstream
The product rules already treat storage as shared platform infrastructure across many business-owned file contexts. That means storage enforcement must happen in shared upload infrastructure, not just inside one route.
Required capabilities
- canonical organization-level storage usage meter in bytes
- canonical organization-level storage allocation in bytes
- included storage from plan tier
- purchased storage add-ons
- 2 GB trial cap
- upload-block enforcement when allocation is exceeded
- self-serve add-on cap at
+1 TB - custom-contact flow above
+1 TB
Enforcement point
Do not enforce storage only in UI.
Enforce it at the shared backend upload boundary so all uploads respect the same limit, including:
- Drive uploads
- contract attachments
- billing attachments
- project files
- other file-backed features
Recommended implementation shape
- create an organization storage summary query/mutation layer in backend
- derive usage from owned file assets / storage metadata
- store purchased storage add-ons as billing-owned data
- refuse new upload URL generation when allocation is exhausted
- surface allocation and overage state in
org/settings/usageand billing
Workstream 6: Billing page and checkout UX
Update the org SaaS billing surface to reflect the new commercial model.
Primary route:
apps/app/src/app/[locale]/org/(dashboard)/settings/billing/page.tsx
Required UX updates
- show Basic / Pro / Business plan cards
- show monthly and annual pricing
- show current included seats and storage
- show add-on state for seats and storage
- show trial eligibility and upgrade-trial eligibility
- show downgrade blockers before confirm
- remove standard Enterprise plan references from the UI
- preserve the current separation from Stripe client billing
Copy rule
All user-facing text changes must go through locale dictionaries in apps/app/messages/*.json or the relevant locale system in app code.
Workstream 7: Provider sync and webhook handling
Once the pricing model changes, provider reconciliation must stay correct.
Required updates
- map new plan ids to provider products
- support annual plan products
- support add-on products or quantities for storage and possibly seats
- keep app-side entitlement snapshots consistent when provider state changes
- preserve cancellation, reactivation, plan change, and checkout return behavior
Important rule
Do not let add-ons corrupt the core plan snapshot.
The base subscription tier and add-on quantities must reconcile independently so the app can answer:
- what base plan is active?
- how many extra seats are purchased?
- how much extra storage is purchased?
- what is the effective limit right now?
Workstream 8: Usage reporting and policy enforcement
Usage is already visible in org/settings/usage, but it currently centers on plan state and feature visibility, not full commercial packaging.
Add to usage reporting
- included seats
- purchased extra seats
- active seat usage
- included storage
- purchased storage add-ons
- current storage usage
- upload-block state
- downgrade-block state
Add to enforcement surfaces
- org invite flows
- storage-backed upload flows
- settings surfaces for gated features
Workstream 9: Tests and rollout safety
Test coverage required
Backend:
- tier migration compatibility
- trial eligibility and one-time upgrade-trial logic
- seat allocation math
- storage allocation math
- downgrade blockers
- add-on reconciliation
- webhook/provider sync with base plan plus add-ons
App:
- billing page renders correct plan matrix
- usage page shows seat/storage allocation correctly
- invite flow blocks when seat limit is reached
- upload flow blocks when trial or paid storage limit is exceeded
Existing test areas to extend
packages/backend/tests/org/billing/*apps/app/tests/org/billing/*apps/app/tests/e2e/billing/*
Recommended Delivery Sequence
Phase 1: Pricing model foundation
- finalize canonical product definition
- decide internal non-paid state name
- map feature matrix to entitlements
- map provider product structure for monthly, annual, and add-ons
Exit condition:
- all pricing rules are representable in code without ambiguity
Phase 2: Backend tier migration
- widen schema and unions
- migrate
basic/enterpriseassumptions - update plan comparison and trial logic
- update platform and usage summaries
Exit condition:
- backend can correctly represent the new pricing ladder
Phase 3: Billing UX refresh
- update
org/settings/billing - update locale copy
- update checkout and plan-change flows
- show seat and storage allocation state
Exit condition:
- org admins can understand and purchase the new plans in-app
Phase 4: Seat commercialization
- implement seat accounting
- enforce invite limits
- support paid seat expansion for Pro and Business
Exit condition:
- seat limits are both visible and enforced
Phase 5: Storage commercialization
- implement storage metering
- implement add-on purchases
- enforce upload blocking at the shared backend boundary
Exit condition:
- storage policy is enforced consistently across all upload surfaces
Phase 6: Hardening
- expand automated tests
- run migration rehearsal in non-production
- verify provider reconciliation and rollback plan
Exit condition:
- the pricing model is safe to launch without silent entitlement drift
Recommended First Ticket Stack
-
Pricing model decision pass
- lock internal tier vocabulary
- lock add-on packaging model
- lock seat counting rules
- lock downgrade rules
-
Canonical pricing config
- create one source of truth for plans, intervals, add-ons, and feature matrix
-
Subscription vocabulary migration
- remove overloaded
basic - remove standard self-serve
enterprise - update validators, snapshots, labels, and tests
- remove overloaded
-
Entitlement expansion
- add branding, white-label, SSO, internal-team, and storage entitlements
-
Billing page refresh
- update org settings billing UI for Basic / Pro / Business plus monthly/annual
-
Seat enforcement
- usage math, billing UI, and invite gating
-
Storage enforcement
- org allocation summary, add-ons, and upload-boundary blocking
Non-Goals For This Project
- migrating org SaaS billing from Polar to Stripe
- reopening agency-operated client billing architecture
- rebuilding Stripe-hosted payment-method management for client billing
- implementing platform-wide enterprise sales CRM workflows
Final Recommendation
Treat this as a structured pricing-platform project, not a copy refresh.
The correct order is:
- canonical pricing model
- backend tier migration
- entitlement update
- billing UI refresh
- seat commercialization
- storage commercialization
- test hardening and rollout rehearsal
If we do it in that order, we can keep the existing Polar SaaS billing baseline intact while safely moving the app to the pricing strategy in docs/app/product/pricing-strategy.md.