# Analytics & usage audit (Descrybe v2) Date: 2026-08-04 Scope: company billing usage, admin analytics, meters vs live Postgres. ## Surfaces | Surface | Path / API | Role | |---------|------------|------| | Company billing / usage | `/billing` → `GET /api/billing/credits`, `GET /api/billing/usage?range=` | Tenant | | Dashboard meters | `/dashboard` → `/api/auth/me` credits + product count | Tenant | | Admin overview | `/admin` → summary from `/api/admin/analytics` | Platform admin | | Admin analytics | `/admin/analytics` → `GET /api/admin/analytics?days=` | Platform admin | | Admin billing | `/admin/billing` | Plans / credits / cycles | ## Bugs found 1. **Stale cycle override on company usage** `UsageSummary` preferred the latest `billing_cycles` row (`credits_used=22`, `products_processed=12`, June–July window) over live wallet and catalog. Demo wallet was `used_credits=0` with **~4323+** processed products and an **Aug–Sep** `company_plans` cycle. 2. **Misleading empty / range UI on `/billing`** “Usage history” gated on `credits_used > 0`, so a rich catalog with zero wallet spend looked empty. The 7d/30d selector did not change any numbers. 3. **Admin provider cards at zero on running binary** Disk already had `ai_provider_mode` rollups; the old `api.exe` still returned stub zeros. After rebuild, internal mode matches DB (`internal` on all products/jobs). 4. **Feeds missing from admin volume** Input/export feed counts were not exposed on the analytics summary. ## Fixes ### `GET /api/billing/usage?range=7d|30d|cycle|all` (default `30d`) - **Credits** always from live `credit_balances` (not historical cycle rows). - **Products / tokens** filtered by range; `products_total` is all-time catalog. - **Cycle dates** from active `company_plans` (`billing_cycle_start` / `next_billing_date`). - **Feeds** (`feeds_input`, `feeds_export`) and `jobs_total` from live tables. - **Series** (products + tokens by UTC day) for 7d / 30d / cycle. - Notes clarify that credit debits are not daily-ledgered yet. ### Admin analytics - Summary includes `feeds_input` / `feeds_export`. - Provider breakdown + detail + per-day class series from `ai_provider_mode`. - Notes updated; billing-cycle table labeled as historical rollups that may lag the active plan window. ### UI - `/billing`: catalog card, range metric cards, honest empty states, range-driven chart via `AdminSeriesChart`. - `/admin/analytics`: feed counts on Volume card; clearer cycle table copy. ## Demo verification (Local Demo Co) Against `postgres://…@localhost:5433/descrybe` after rebuild, logged in as `demo@descrybe.local`: | Meter | API | DB | |-------|-----|-----| | Wallet used / total | 0 / 1_000_000 | 0 / 1_000_000 | | Products (all) | 4326 | 4326 | | Input / export feeds | 12 / 5 | 12 / 5 | | Plan cycle | 2026-08-04 → 2026-09-04 | `company_plans` | | Platform products | 7225 | 7225 | | Platform credits used | 26 | 26 | | Platform feeds in/out | 35 / 12 | 35 / 12 | | Provider internal tokens/products | 11_383_374 / 7225 | `ai_provider_mode=internal` | Previously the usage API returned **22 credits / 12 products** from a stale `billing_cycles` row — fixed. ## Remaining limitations - No per-day **credit** ledger; range filters apply to products/tokens only. - `billing_cycles` historical rows can lag or disagree with the active plan window; they remain an admin history table, not the tenant source of truth. - Token series for migrated catalogs often clumps on import day(s). ## Verification commands ```powershell cd apps/api go test ./internal/billing/ -run ParseUsageRange -count=1 go build -o bin/api.exe ./cmd/api cd ../web npm run check ``` Demo smoke (after API restart): ```powershell # login as demo@descrybe.local then: # GET /api/billing/usage?range=30d # GET /api/admin/analytics?days=30 ```