API: Analytics
Controller: admin-analytics.controllers.ts. Mount admin/analytics.
All endpoints require analytics:read.
Overview
Section titled “Overview”GET /v1/admin/analytics/overview
Section titled “GET /v1/admin/analytics/overview”Query: dateFrom, dateTo, market.
Response:
{ "dau": 12345, "wau": 45678, "mau": 98765, "installsToday": 543, "registrationsToday": 421, "payingUsersToday": 89, "arpu30d": 0.42, "arppu30d": 5.10, "retentionCohorts": [ { "installWeek": "2026-06-01", "cohortSize": 8765, "d1": 0.48, "d7": 0.24, "d30": 0.12 } ]}Revenue
Section titled “Revenue”GET /v1/admin/analytics/revenue
Section titled “GET /v1/admin/analytics/revenue”Query: dateFrom, dateTo, rails[], countries[], kind.
Response:
{ "period": { "start": "...", "end": "..." }, "grossCents": 12345678, "netCents": 10234567, "refundsCents": 234567, "byDay": [ { "day": "2026-07-01", "gross": 45678, "net": 40234, "refunds": 1200 } ], "byRail": [ { "rail": "apple", "gross": 5000000, "net": 3500000 } ], "byCountry": [ { "country": "PH", "gross": 3000000, "net": 2600000 } ], "arpu": 0.42, "arppu": 5.10, "refundRate": 0.019}Content
Section titled “Content”GET /v1/admin/analytics/content
Section titled “GET /v1/admin/analytics/content”Query: dateFrom, dateTo, market, genreSlug, sort, order, cursor, limit.
Response: paginated series list with per-series metrics (starts, completes, completion %, unlocks, coins burned, watch seconds).
GET /v1/admin/analytics/content/series/:id
Section titled “GET /v1/admin/analytics/content/series/:id”Query: dateFrom, dateTo, market.
Response: per-episode drop-off curve + unlock conversion for the series.
{ "seriesId": "...", "period": {}, "episodes": [ { "episodeNumber": 1, "starts": 10000, "completes": 8500, "paywallHits": 0, "unlocks": 0, "cliffhangerScore": 65 }, { "episodeNumber": 4, "starts": 3200, "completes": 2100, "paywallHits": 5000, "unlocks": 850, "cliffhangerScore": 80 } ]}Funnel
Section titled “Funnel”GET /v1/admin/analytics/funnel
Section titled “GET /v1/admin/analytics/funnel”Query: dateFrom, dateTo, market, installCohort.
Response:
{ "steps": [ { "name": "preview_view", "count": 100000, "conversionFromPrev": 1.0, "conversionFromTop": 1.0 }, { "name": "ep1_start", "count": 40000, "conversionFromPrev": 0.40, "conversionFromTop": 0.40 }, { "name": "paywall_hit", "count": 12000, "conversionFromPrev": 0.30, "conversionFromTop": 0.12 }, { "name": "unlock", "count": 1800, "conversionFromPrev": 0.15, "conversionFromTop": 0.018 }, { "name": "repeat_unlock", "count": 1080, "conversionFromPrev": 0.60, "conversionFromTop": 0.0108 } ]}Rollups (admin-only)
Section titled “Rollups (admin-only)”POST /v1/admin/analytics/rollups/run
Section titled “POST /v1/admin/analytics/rollups/run”Permission: analytics:read (also system:write in some builds).
Manual trigger of the hourly rollup cron. Use for local testing; production relies on the cron.
Data sources
Section titled “Data sources”- All dashboards read from rollup tables (
rollup_revenue_daily,rollup_series_daily,rollup_funnel_daily). - Rollups are built by the
rollups-hourlycron (Cron jobs). - Retention cohorts are computed on the fly from
analytics_event(30-day cap for drill-down).