Task 11 — /usage route extension
Depends on: 06 (and ideally 07–10 for real data, but it can ship earlier)
Unblocks: 16
Goal
/usage returns the legacy quota payload AND a new stats payload with
three cost lenses, query-param filtering, and a missing_pricing list.
Scope
src/routes/usage/route.ts accepts these query params:
from / to — millisecond timestamps; default last 30 days
account — filter to one account name
model — filter to one model id
endpoint — filter to one of the three endpoint values
group — day | model | account | endpoint (default day)
lens — historical | current | timeline (default historical)
Response shape: see design doc 03 §/usage route.
SQL templates
Three pre-built statements per lens. The WHERE clause is composed from the
filter params; bind variables only — never string-interpolate user input.
missing_pricing:
SELECT DISTINCT model_id
FROM usage_events
WHERE model_id NOT IN (SELECT model_id FROM model_pricing)
AND ts BETWEEN ? AND ?;
quota:
- For each account in the pool, call
getCopilotUsage(account).
- Return a list under
quota.byAccount. Keep a top-level quota field as
the merged / fan-out summary for backwards compat — pick the first
account's payload if a single object is needed.
Definition of Done
Task 11 —
/usageroute extensionDepends on: 06 (and ideally 07–10 for real data, but it can ship earlier)
Unblocks: 16
Goal
/usagereturns the legacyquotapayload AND a newstatspayload withthree cost lenses, query-param filtering, and a
missing_pricinglist.Scope
src/routes/usage/route.tsaccepts these query params:from/to— millisecond timestamps; default last 30 daysaccount— filter to one account namemodel— filter to one model idendpoint— filter to one of the three endpoint valuesgroup—day|model|account|endpoint(defaultday)lens—historical|current|timeline(defaulthistorical)Response shape: see design doc 03 §
/usageroute.SQL templates
Three pre-built statements per lens. The
WHEREclause is composed from thefilter params; bind variables only — never string-interpolate user input.
missing_pricing:quota:getCopilotUsage(account).quota.byAccount. Keep a top-levelquotafield asthe merged / fan-out summary for backwards compat — pick the first
account's payload if a single object is needed.
Definition of Done
curl /usagewithout params returns bothquotaandstats.?lens=currentshows different totals than?lens=historicalafter apricing sync that changed prices.
?from=&to=correctly bounds the dataset.?group=modelreturns rows keyed by model.pages/index.html?endpoint=...still loads (itignores the new
statsfield gracefully — task 16 will use it).docs/tasks/11-usage-route.mddocs/design/