Skip to main content

Overview

PassAgent offers three plan tiers: Free, Personal, and Team. Each tier defines limits on vault size, family members, rotation policies, and access to advanced features. Plan enforcement happens server-side and returns structured error responses when limits are exceeded.

Plan comparison

FeatureFreePersonalTeam
Max passwords50UnlimitedUnlimited
Family members066
Rotation policies11Unlimited
Team sharingNoNoYes
Advanced audit logNoNoYes
SSO integrationNoNoYes
AI password resetsYesYesYes
Travel FortressYesYesYes
Breach monitoringYesYesYes
Passkey supportYesYesYes

Plan resolution

PassAgent determines your plan tier through a two-step lookup.
1

Check the subscriptions table

The system queries for an active or trialing subscription linked to your user ID. If a subscription with plan family_monthly or family_yearly is found with status active or trialing, you are on the Personal tier.
2

Fallback to family membership

If no direct subscription exists, the system checks whether you are an active member of a family that has a valid subscription. If the family’s subscription_status is active or trialing and current_period_end has not passed, you are on the Personal tier.
3

Default to free

If neither lookup finds an active subscription, you are on the Free tier.
Plan resolution fails open. If the subscription lookup encounters a database error or timeout, the system returns the free tier rather than blocking access. This prevents infrastructure issues from locking users out of their vaults.

Subscription plans

Plan IDBilling cycleTier
family_monthlyMonthlyPersonal
family_yearlyAnnualPersonal

Usage enforcement

Password limit

When you add a new password, the server calls enforcePasswordLimit(). If you are on the Free tier and have reached the 50-password cap, the request is rejected.
{
  "error": "Plan limit reached",
  "message": "Free accounts can store up to 50 passwords. Upgrade to unlock unlimited storage.",
  "code": "PLAN_LIMIT_PASSWORDS",
  "currentCount": 50,
  "limit": 50,
  "upgradeUrl": "/pricing"
}
The password limit only applies to the Free tier. Personal and Team plans have unlimited password storage.

Family member limit

When adding a new family member, the server calls enforceFamilyMemberLimit(). If the family has reached its maximum member count (6 for both Personal and Team), the request is rejected.
{
  "error": "Family member limit reached",
  "message": "Your family plan supports up to 6 members.",
  "code": "PLAN_LIMIT_FAMILY_MEMBERS",
  "currentCount": 6,
  "limit": 6
}

Rotation policy limit

Rotation policies control automated password rotation schedules. Free and Personal tiers are limited to 1 rotation policy. Team plans have no limit.

Error handling

All plan limit errors share a consistent structure that clients can detect programmatically.
interface PlanLimitError {
  code: string        // "PLAN_LIMIT_PASSWORDS" or "PLAN_LIMIT_FAMILY_MEMBERS"
  message: string     // Human-readable description
  currentCount: number // Current usage count
  limit: number       // Plan maximum
  upgradeUrl: string  // Path to the pricing page ("/pricing")
}

Feature gates

Beyond usage limits, certain features are gated by plan tier.
Feature gateCheckFreePersonalTeam
Team sharinglimits.teamSharingDisabledDisabledEnabled
Advanced auditlimits.advancedAuditDisabledDisabledEnabled
SSO integrationlimits.ssoIntegrationDisabledDisabledEnabled
To check feature availability programmatically:
import { getUserLimits } from "@/lib/security/feature-gate"

const limits = await getUserLimits(userId)
if (!limits.teamSharing) {
  // Return 403 or show upgrade prompt
}

Billing integration

Subscriptions are managed through Stripe. The billing webhook handler (/api/billing/webhook) processes subscription lifecycle events and updates the subscriptions table. Promo codes can create family memberships directly, bypassing the Stripe subscription flow.
The following Stripe subscription statuses are treated as active:
  • active — subscription is current and paid
  • trialing — subscription is in a free trial period
All other statuses (past_due, canceled, unpaid, incomplete, incomplete_expired, paused) are treated as inactive and resolve to the Free tier.

Key files

FilePurpose
lib/security/feature-gate.tsPlan tier resolution, limits lookup, enforcement functions
lib/utils/plan-limit-error.tsPlan limit error type and detection utility
app/api/billing/webhook/route.tsStripe webhook handler for subscription events
app/api/billing/checkout/route.tsStripe checkout session creation