# Sprint 1.5 — Stats API Operator-visible campaign metrics via REST, backed by real-time `DailySchedule` counters and nightly `DailyStat` rollups. ## What shipped - **`DailyStat` model** — materialized daily rollups with `replyRateActual`, `inboxPlacementRate` - **`stats.service.ts`** — today, daily time series, range summary, workspace overview - **Stats routes:** - `GET /api/campaigns/:id/stats/today` - `GET /api/campaigns/:id/stats/daily?from=&to=` - `GET /api/campaigns/:id/stats?range=7d|30d|all` - `GET /api/workspace/overview` - **`stats-rollup.job.ts`** — hourly cron, finalizes yesterday at 00:xx campaign TZ - **`POST /api/jobs/rollup/run`** — manual rollup trigger - **Logs filter** — `GET /api/logs?campaignId=` ## Response example (`/stats/today`) ```json { "campaignId": "...", "date": "2026-06-25", "dayIndex": 12, "planned": { "sends": 18, "replies": 6 }, "actual": { "sends": 15, "replies": 5, "spamDetected": 2, "spamRescued": 2 }, "rates": { "reply": 33.3, "inbox": 86.7 }, "recipe": "grow", "onTrack": true } ``` ## FillCare manual QA 1. Active Grow campaign with sends in progress 2. `GET /api/campaigns/:id/stats/today` — verify planned vs actual 3. `GET /api/workspace/overview` — see hello@ primary at a glance 4. Compare stats counts to `GET /api/logs?campaignId=:id` ## Key files - `src/services/stats.service.ts` - `src/jobs/stats-rollup.job.ts` - `src/routes/stats.routes.ts`