Cycle Usage View
The cycle usage view gives org members a real-time look at credit consumption on the billing settings tab. It aggregates credits across all three pools — op-specific tier allowances, general plan credits, and purchased credits — into a single dashboard with a per-operation breakdown.
API Endpoint
Section titled “API Endpoint”GET /api/billing/usage
Section titled “GET /api/billing/usage”Returns aggregated usage for the current billing cycle. No auth gate — any org member can call it (matches the get_org_subscription_api access pattern).
Handler: get_org_usage_api() in src/mods/billing/api/get_org_usage_api.rs
Response type: OrgUsageSummary
pub struct OrgUsageSummary { pub has_subscription: bool, pub cycle_start: String, // RFC-3339 billing period start pub cycle_end: String, // RFC-3339 billing period end pub credits_granted: i32, // Snapshot from org_budget.initial_included_credits pub credits_spent: i32, // Total credits across all three pools pub included_credits_remaining: i32, // Plan pool balance (clamped at 0) pub credits_purchased_this_cycle: i32, // Credits bought this cycle (inflow) pub purchased_credits_spent: i32, // Credits consumed from purchased pool (outflow) pub by_operation: Vec<OpUsageRow>, // Sorted by highest spend first pub overdraft_used: i32, // Current overdraft debt (0 when not in overdraft) pub overdraft_limit: i32, // System-wide overdraft ceiling}
pub struct OpUsageRow { pub operation_type: OperationType, pub credits_spent: i32, // Credits across all three pools for this op}Three-pool aggregation
Section titled “Three-pool aggregation”The endpoint sums credits from all three pools per operation type:
| Pool | Column | Source |
|---|---|---|
| Op-specific tier allowance | free_units_used | Per-op credits included with the tier |
| General plan credits | included_credits | Seat-based plan allocation |
| Purchased credits | purchased_credits | Credit packs bought by the org |
SELECT operation_type, SUM(included_credits) AS included_sum, SUM(purchased_credits) AS purchased_sum, SUM(free_units_used) AS free_units_sumFROM operationWHERE organization_id = ? AND created_at >= ?GROUP BY operation_typeCredit grant snapshot
Section titled “Credit grant snapshot”The credits_granted field reads from org_budget.initial_included_credits — a snapshot of the gross tier credit grant frozen at cycle start. This includes both seat-based credits and the sum of all per-op allowances from the tier.
let credits_granted = budget_credits .map(|(_, _, initial)| initial.max(0)) .unwrap_or(0);The snapshot is written by reset_cycle_budget_service::reset_cycle_budget_in_txn() on every cycle reset:
let gross_tier_allowance_i64 = (tier.credits_per_seat as i64) .saturating_mul(seat_count as i64) .saturating_add(sum_tier_per_op_allowances(&tier));
budget.initial_included_credits = Set(gross_tier_allowance);This anchors both the numerator (included_credits_remaining) and denominator (credits_granted) to the same point in time. Admin tier edits mid-cycle no longer corrupt the “Credits included: X / Y” display — the denominator stays fixed until the next cycle reset.
Purchased credits: inflow vs. outflow
Section titled “Purchased credits: inflow vs. outflow”Two distinct metrics track purchased credits:
credits_purchased_this_cycle— what was bought this cycle (fromcredit_pack_purchasetable)purchased_credits_spent— what was consumed from the purchased pool (fromoperationtable)
These answer different questions: “How much did we buy?” vs. “How much of our purchased credits did we use?”
Query strategy
Section titled “Query strategy”- Load subscription (no tier join needed —
credits_grantedcomes from the budget snapshot) - Load
org_budgetwithselect_only()(3 columns:included_credits,purchased_credits,initial_included_credits) - Aggregate
operationtable withGROUP BY operation_typebounded by cycle start - Aggregate
credit_pack_purchasefor credits bought this cycle - Sort results by
credits_spentdescending
Returns OrgUsageSummary::default() when no subscription exists.
Shared Billing Helpers
Section titled “Shared Billing Helpers”Three helper functions in src/mods/billing/services/record_operation_service.rs ensure consistent arithmetic across the customer and admin billing APIs.
clamp_i64_to_i32()
Section titled “clamp_i64_to_i32()”Safely narrows i64 aggregates to i32. Clamps to i32::MAX and emits a structured warn log on overflow.
pub(crate) fn clamp_i64_to_i32( value: i64, label: &'static str, org_id: Uuid, op_type: Option<OperationType>,) -> i32Use this whenever DB SUM() aggregates or multiplicative totals need to fit in an i32 API response field.
sum_tier_per_op_allowances()
Section titled “sum_tier_per_op_allowances()”Sums all per-operation credit allowances from a subscription_tier row. Returns the total as i64.
pub(crate) fn sum_tier_per_op_allowances( tier: &schemas::subscription_tier::Model,) -> i64Enumerates every *_included field on the tier (voice, SMS, AI tiers, email, etc.). Update this in lockstep with tier_allowance() when adding new OperationType variants.
overdraft_depth()
Section titled “overdraft_depth()”Returns the positive magnitude of a negative included_credits balance — the amount currently owed against overdraft.
pub(crate) fn overdraft_depth(included_credits: i32) -> i32 { (-included_credits).max(0)}UI Component
Section titled “UI Component”CycleUsageSection renders inside BillingContent, below the current-plan card. It only appears when a subscription exists.
File: src/mods/billing/components/cycle_usage_section_component.rs
Component hierarchy
Section titled “Component hierarchy”CycleUsageSection ← exported, no props, owns use_resource└─ CycleUsageContent ← receives OrgUsageSummary ├─ OverdraftBanner ← conditional: overdraft_used > 0 ├─ Three-column card │ ├─ Credits used ← total spent, with plan/purchased split │ ├─ Credits included ← remaining / granted + progress bar │ └─ Credits purchased ← bought this cycle ├─ Empty state card ← when by_operation is empty └─ OperationsBreakdown ← table of OpUsageRow items └─ OperationBreakdownRowThree-column card layout
Section titled “Three-column card layout”The card displays three balanced stats:
| Column | Value | Detail |
|---|---|---|
| Credits used | credits_spent | Inline split: (X from plan · Y purchased) when purchased > 0 |
| Credits included | included_credits_remaining / credits_granted | Progress bar tracking plan pool depletion |
| Credits purchased | credits_purchased_this_cycle | Credits bought this cycle (inflow, not outflow) |
On mobile, columns stack vertically.
Progress bar
Section titled “Progress bar”The bar tracks plan pool depletion only — purchased credit spend does not advance it.
let progress = if credits_granted > 0 { (1.0 - (included_credits_remaining as f64 / credits_granted as f64)) .clamp(0.0, 1.0)} else { 0.0};- 0% — fresh cycle, full plan pool
- 100% — plan pool fully consumed
- Overdraft is communicated by a separate
OverdraftBanner, not the progress bar
Per-operation breakdown
Section titled “Per-operation breakdown”The breakdown groups operations into two categories:
Non-AI rows render flat — one row per OperationType showing credits and share %.
AI rows (all 24 Ai* variants plus AiTextLegacy) collapse under a single “AI usage” row. This row shows the aggregated AI credit total and its share of the overall cycle spend. Click the chevron (>) to expand per-feature rows sorted by spend descending, with share percentages computed against the AI total (not the overall total). Click again to collapse.
Each row shows:
- Operation name — derived from
OperationTypedisplay - Credits — total credits across all three pools
- Share % — relative to overall total (top-level) or AI total (expanded detail rows)
The collapsible layout is mobile-responsive — indented detail rows stack cleanly on narrow viewports.
Empty state
Section titled “Empty state”When the org has a subscription but zero operations this cycle, a dashed-border card displays: “No usage this cycle yet” with a hint that activity appears once calls, messages, or AI actions run.
Admin API Consistency
Section titled “Admin API Consistency”The admin billing API (get_admin_org_billing_api) uses the same three-pool aggregation and shared helpers. Both endpoints produce consistent credit totals.
Related Pages
Section titled “Related Pages”- Billing Module Overview — architecture and credit waterfall model
- Operation Metering — how
record_operation()writes the data this view reads - Credit Packs — purchasing and consuming credit packs