Phase 5 — Organization Management & Subscription Gating
Goal: Full multi-user org support. Hard usage limits enforced. Subscription management via Stripe.
5.1 Backend — Organization Management
GET /v1/orgs/:id
PATCH /v1/orgs/:id — update name, logo, billingEmail
GET /v1/orgs/:id/members
POST /v1/orgs/:id/members/invite — invite a recruiter by email
PATCH /v1/orgs/:id/members/:userId — change member role
DELETE /v1/orgs/:id/members/:userId
GET /v1/orgs/:id/features — current featureSnapshot5.2 Backend — Plan Management + featureSnapshot Sync
GET /v1/plans — list all active plans (public, no auth)
POST /v1/orgs/:id/plan — change plan (admin only)On plan change:
typescript
const plan = await Plan.findOne({ key: newPlanKey });
await Organization.findByIdAndUpdate(orgId, {
planKey: newPlanKey,
featureSnapshot: {
maxActiveJobs: plan.features.maxActiveJobs,
maxCandidatesPerJob: plan.features.maxCandidatesPerJob,
maxInterviewsPerMonth: plan.features.maxInterviewsPerMonth,
advancedAnalytics: plan.features.advancedAnalytics,
customBranding: plan.features.customBranding,
apiAccess: plan.features.apiAccess,
},
});All stage types remain available — only usage limits change.
5.3 Backend — Hard Usage-Limit Enforcement
Usage limit checks added to all three enforcement points (already scaffolded in Phase 0):
typescript
// POST /v1/jobs — active job limit
// POST /v1/interviews — monthly interview count
// POST /v1/interviews — first candidate for a job (maxCandidatesPerJob)See Usage Cap Enforcement deep dive for implementation details.
5.4 Backend — Stripe Webhooks
POST /v1/webhooks/stripe:
customer.subscription.updated→ sync plan → updateOrganization.planKey+featureSnapshotcustomer.subscription.deleted→ downgrade tofree
5.5 Frontend — Org Settings Page
Route: /settings/organization
src/pages/settings/OrganizationSettings.tsx:
- Org name, logo upload, billing email
- Members table: list, invite, remove, role change
- Current plan display + "Upgrade Plan" CTA
5.6 Frontend — Billing Page
Route: /settings/billing
src/pages/settings/BillingSettings.tsx:
- List plans from
GET /v1/plans(dynamic — no hardcoded plan names) - Show current plan, feature comparison table
- Stripe Checkout integration
Phase 5 — Acceptance Criteria
- [ ] Org admin can invite team members by email; invited users appear in
Organization.members - [ ]
GET /v1/stage-typesreturns all 6 stage types regardless of plan - [ ] Job creation returns 403 when
maxActiveJobslimit is reached - [ ] Interview creation returns 403 when
maxInterviewsPerMonthlimit is reached - [ ] Candidate addition returns 403 when
maxCandidatesPerJoblimit is reached - [ ] Plan upgrade via Stripe updates
featureSnapshot; higher limits take effect immediately - [ ]
advancedAnalytics,customBrandingflags gate premium features correctly - [ ] Billing page reads plan names from
GET /v1/plans— no hardcoded plan comparison table