Skip to content

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

LevelBound toControls
User.planIdUserNumber of organizations + standalone teams
Organization.planIdOrganizationTeams, projects, storage within the org

Grace Period on Downgrade

When downgrading to a plan with lower limits, a 30-day grace period applies:

  • previousPlanId + planChangedAt are saved on User/Organization
  • QuotaService.getEffectiveLimitsForUser/Org() returns MAX of old and new plan limits
  • After 30 days, strict new plan limits apply
  • gracePeriodDaysLeft is returned in QuotaInfo
  • Upgrades are immediate (clears previousPlanId)

API Endpoints

MethodPathDescription
GET/api/plansList available plans
GET/api/users/me/quotaUser quota info
PATCH/api/users/me/planChange user plan
GET/api/organizations/by-slug/:slug/quotaOrg quota info
PATCH/api/organizations/by-slug/:slug/planChange org plan
GET/api/teams/by-slug/:teamSlug/quotaTeam quota info

Quota Enforcement

Quotas are checked before resource creation:

  • checkCanCreateOrganization() — user org limit
  • checkCanCreateTeam() — team limit in org
  • checkCanCreateProject() — project limit
  • checkStorageQuota() — 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 page
  • PlanTable.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