Plans & Quotas
Planner uses a dual-level plan system with separate quotas for users and organizations.
Model
Plan
├── name, description
├── price (monthly)
├── maxOrgs, maxStandaloneTeams
├── maxTeamsPerOrg, maxProjectsPerOrg
├── maxProjectsPerTeam, maxMembersPerTeam
├── storagePerOrg, storagePerTeam (in bytes)
├── public (false — hidden from non-admins, for Enterprise)
└── features (JSON feature list)Two Plan Levels
| Level | Bound to | Controls |
|---|---|---|
| User.planId | User | Number of organizations + standalone teams |
| Organization.planId | Organization | Teams, projects, storage within the org |
Grace Period on Downgrade
When downgrading to a plan with lower limits, a 30-day grace period applies:
previousPlanId+planChangedAtare saved on User/OrganizationQuotaService.getEffectiveLimitsForUser/Org()returns MAX of old and new plan limits- After 30 days, strict new plan limits apply
gracePeriodDaysLeftis returned inQuotaInfo- Upgrades are immediate (clears previousPlanId)
API Endpoints
| Method | Path | Description |
|---|---|---|
GET | /api/plans | List available plans |
GET | /api/users/me/quota | User quota info |
PATCH | /api/users/me/plan | Change user plan |
GET | /api/organizations/by-slug/:slug/quota | Org quota info |
PATCH | /api/organizations/by-slug/:slug/plan | Change org plan |
GET | /api/teams/by-slug/:teamSlug/quota | Team quota info |
Quota Enforcement
Quotas are checked before resource creation:
checkCanCreateOrganization()— user org limitcheckCanCreateTeam()— team limit in orgcheckCanCreateProject()— project limitcheckStorageQuota()— storage limit
Frontend
Plans Store
typescript
const store = usePlansStore()
const { plans, currentPlan, userQuota } = storeToRefs(store)
await store.fetchPlans()
await store.fetchUserQuota()
await store.selectPlan(planId)Components
PlanSelectionView.vue(/settings/plan) — plan selection pagePlanTable.vue— plan comparison table- Grace period banner — warning about grace period on downgrade
Key Implementation Details
- Enterprise plan is hidden from regular users (
public: false) - Admins see all plans via
GET /admin/plans - Storage is recalculated daily at 3:00 AM (cron) from the files table
QuotasProcessor(Bull queue) — background cron for grace period expiration