GenerateSaaS

Plan grants

Per-plan credit allotments, trial withholding, and the daily recurring re-grant job.

Each plan in pricingConfig.plans may carry these credit fields.

KeyTypeDefaultDescription
creditsnumber0Credits the plan includes per cycle.
creditIntervalnumber (days)0How often credits re-grant. Omit or 0 for a single grant.
grantCreditsOnTrialbooleanunset = withholdGrant credits during a trialDays trial.

The shipped recurring plans grant free: 5, starter: 10, pro: 40, business: 100, all on creditInterval: 30; enterprise grants 500 exactly once (no interval - its lifetime price could never fund a perpetual grant). Resolve them with getCreditsForPlan(planId) / getCreditIntervalForPlan(planId), which return 0 when absent.

grantCreditsOnTrial is opt-in and checked as === true: up-front trial credits are farmable across throwaway trials. Left unset, credits are withheld until the first paid grant.

Recurring re-grants

The recurring-credits job (packages/api/src/functions/billing/recurring-credits.ts) runs daily on cron 0 0 * * *.

Load users on a credit-carrying plan whose payment has cleared. Trialing, past-due, and unpaid subscriptions are excluded, as are banned users.
Compare creditsLastGrantedAt + creditInterval against now; skip those not due, and lifetime holdings already granted once.
Claim a per-day idempotency key, re-read the account and skip it when its plan changed since the first step (the next run grants on the new plan), then grant via addCredits(...) - which stamps creditsLastGrantedAt in the same update - and write a CREDITS_ADDED audit entry.

Every grant also fires the credits_added notification trigger (gated by config.notifications.enabled), reaching the owner inside an organization since that is whose balance moved. See Background jobs for the scheduler and Audit logs for the entries.

On this page