Metering a Custom Feature¶
After this guide you can put any action behind a per-plan usage limit — and, optionally, let credit top-ups extend it the way AI tokens do. See Plans & Usage for the engine this builds on.
1. Define the feature¶
Add it to both the FeatureKey enum and the PlanFeatures model in backend/app/model/plan_model.py:
class FeatureKey(str, Enum):
MAX_NOTES = "max_notes"
TOKEN_LIMIT = "token_limit"
ENABLE_AI = "enable_ai"
MAX_PROJECTS = "max_projects" # new
class PlanFeatures(BaseModel):
max_notes: int | None
token_limit: int | None
enable_ai: bool | None
max_projects: int | None # new
2. Set limits per plan¶
Add max_projects to each plan's features map — via the admin Plans page or seed data (see Plans & Usage).
3. Enforce the limit¶
Wrap the action with the generic usage service — check before, record after:
if not await usage_service.check_quota_for(org_id, FeatureKey.MAX_PROJECTS, 1):
raise QuotaExceeded(FeatureKey.MAX_PROJECTS)
project = await project_service.create(...)
await usage_service.update_usage(org_id, FeatureKey.MAX_PROJECTS, 1)
That's all it takes to meter a feature against the plan limit, reset each billing period.
4. (Optional) Let credits top it up¶
Today only AI tokens fall back to the credit balance. To let credits extend another feature, mirror what AiUsageBillingService does: when the per-period limit is exhausted, check and debit the org's credit balance before blocking the action.
Two caveats, since the credit system is currently AI-token-specific:
- The balance is a single token-denominated pool. If you want a separate balance per feature (e.g. "project credits"), add a per-feature balance rather than reusing the token pool.
- Decide the unit. Reusing the token pool only makes sense if your feature is denominated the same way.
So: metering any feature is built in; credit top-ups for non-AI features are a small, deliberate extension.