Plans & limits
Every plan tier, price, and limit lives in one file:
lib/billing/plans.ts. Both the billing logic and the marketing pricing section
read from it.
lib/billing/plans.ts is the single source of truth. Never hardcode plan
data anywhere else — change it here and it propagates everywhere.
Default tiers
| Plan | Price/mo | Monthly tokens | Messages | Members | Tools | API access | Custom prompt | Rate limit |
|---|---|---|---|---|---|---|---|---|
| Free | $0 | 100,000 | 50 | 1 | ❌ | ❌ | ❌ | 20 req/min |
| Pro | $29 | 2,000,000 | 1,000 | 1 | ✅ | ❌ | ✅ | 60 req/min |
| Team | $79 | 10,000,000 | 5,000 | 10 | ✅ | ✅ | ✅ | 120 req/min |
The shape
export interface Plan {
id: PlanId; // "free" | "pro" | "team"
name: string;
price: number; // USD per month
limits: {
monthlyTokens: number;
monthlyMessages: number;
members: number;
};
features: {
tools: boolean; // agentic tool calling allowed
apiAccess: boolean; // programmatic API access
customSystemPrompt: boolean;
};
rateLimit: { requestsPerMinute: number };
}How limits are enforced
- AI usage metering — every AI call records its token count. Per-call totals roll up into a monthly aggregate for fast limit checks.
- Hard limit — once a workspace hits
monthlyTokens(ormonthlyMessages), further AI calls are blocked until the next cycle or an upgrade. - Soft limit — at ~80% usage, a warning email is sent (when email is enabled).
- Rate limit —
rateLimit.requestsPerMinuteis enforced per plan when the rate-limiting feature (Upstash) is on. - Feature gates —
features.tools,apiAccess, andcustomSystemPromptswitch capabilities on/off by plan.
Customising plans
To change pricing or limits:
- Edit the values in
lib/billing/plans.ts. - Create matching Stripe products/prices and set
STRIPE_PRICE_PRO/STRIPE_PRICE_TEAMto the new price ids. - Restart. The pricing page and billing logic both pick up the change.
To add a tier, extend the PlanId union and the PLANS record, then add a
corresponding Stripe price id env var and wire it where the existing ones are
read.