Analytics Backbone
This page documents the OSS v1 analytics surface tracked by #160.
Live Module Inventory
The repo currently has these active OSS analytics-facing surfaces that matter for v1:
apps/server/api/src/app.module.ts- imports
PostsModule - imports
ContentPerformanceModule - imports
BusinessAnalyticsModule - imports
AnalyticsModule
- imports
- Provider metric ingestion and publication analytics:
- Brand-facing analytics rollups:
- Content-performance feedback loop:
apps/server/api/src/collections/content-performance/content-performance.module.tsapps/server/api/src/collections/content-performance/controllers/performance-summary.controller.tsapps/server/api/src/collections/content-performance/services/performance-summary.service.tsapps/server/api/src/collections/content-performance/services/analytics-sync.service.ts
- Collection-facing business analytics:
- Endpoint-level organization and brand analytics:
- Thin consumers of the same analytics services:
Ownership Notes For V1
The important v1 fact is that analytics is not a single file or a dead stub. There is a real split:
- Post analytics ingestion owns provider fetches and daily
postAnalyticswrites. - Publication analytics owns immediate per-post summaries and optional date-range rows.
- Brand analytics owns brand-scoped overview, platform, and time-series rollups backed by
AnalyticsAggregationService. - Content performance owns top/worst content, platform feedback, posting-time analysis, top hooks, and the
postAnalytics->contentPerformancesync. - Endpoint analytics owns organization/brand leaderboards, overview, platform breakdowns, and raw SQL aggregation over
post_analytics. - Business analytics owns revenue, credit, ingredient, leader, projection, and comparison dashboards.
- Thin consumers such as MCP and agent goals may read analytics, but they should not own the aggregation rules.
That split is primarily why #160 remains open: the v1 story needs the surface documented before deeper OSS/EE extraction work.
Intentional Overlap
Some surfaces read the same postAnalytics data for different users and time horizons:
PostAnalyticsServiceandPerformanceSummaryServiceoverlap on post metrics. The first answers “how did this post do now?”; the second answers “what should the content loop learn from recent complete windows?”AnalyticsAggregationServiceand endpointAnalyticsServiceboth aggregatepostAnalytics. The collection service powers brand relationship endpoints; the endpoint service powers broader organization/brand leaderboards and overview APIs.- Collection and endpoint
BusinessAnalyticsServiceimplementations overlap on business-dashboard shape. Keep both stable for v1, then consolidate or extract after the Cloud/EE billing boundary is clearer. AnalyticsSyncServiceduplicates selectedpostAnalyticsmetrics intocontentPerformanceby design. That copy is the optimization/memory substrate, not a replacement for the source analytics rows.
OSS vs EE Ownership Boundary
For v1, analytics is owned by Core when it is required to complete the self-hosted content loop. Enterprise owns analytics when the feature depends on multi-tenant governance, agency/team operations, or commercial billing controls.
| Analytics surface | V1 owner | Stabilization decision |
|---|---|---|
| Provider metric ingestion for published posts | Core (OSS) | Keep in apps/server/api/src/collections/posts. Self-hosted users need a working publish -> analytics feedback path with their own provider credentials. |
| Publication-level analytics summary | Core (OSS) | Keep PostAnalyticsService.getPostAnalyticsSummary and the posts analytics controller in Core because they verify whether published content performed. |
| Content-performance weekly summaries | Core (OSS) | Keep PerformanceSummaryService in Core for top/worst content, platform summaries, posting-time analysis, top hooks, and week-over-week feedback. |
| Closed-loop performance sync | Core (OSS), EE-extendable | Keep AnalyticsSyncService in Core so analytics can feed optimization and brand memory. EE may add tenant-aware scheduling, retention policy, or governance hooks. |
| Business/operator revenue analytics | Core for current v1 | Keep the existing business analytics response stable for v1. Revisit extraction only after Cloud/EE billing ownership is clearer. |
| Multi-tenant analytics governance | Enterprise (ee/) | Tenant isolation, cross-client agency reporting, audit controls, SSO/team-scoped permissions, and commercial reporting belong under ee/. |
| Billing, quota, and contract reporting | Enterprise / Cloud | Commercial billing analytics can consume Core events, but the reporting and entitlement logic should not move into the OSS content loop. |
Extraction Decisions
- Do not extract
postAnalytics, post analytics controllers,PerformanceSummaryService, orAnalyticsSyncServiceout of Core for v1. - Do not block OSS v1 on a perfect analytics package split; stabilize the existing Core services with smoke paths and docs first.
- Do not move tenant-isolation policy into Core analytics as a hidden requirement. Core remains self-hosted single-tenant by default; EE can wrap or extend the same data flow with organization-level controls.
- Treat the current business analytics module as a compatibility surface for v1. If Cloud billing analytics diverges later, extract commercial reporting behind an EE/Cloud boundary instead of weakening Core analytics.
Stable Analytics Ingestion Pipeline
The v1 ingestion path starts from published content that has a provider externalId and a connected credential:
PostsAnalyticsController.refreshAnalyticsrefreshes one post throughPOST /posts/:postId/refresh-analytics.PostsAnalyticsController.refreshAllAnalyticsrefreshes all published posts for an organization throughPOST /posts/analytics.- Both paths call
PostAnalyticsService.trackPostAnalytics. PostAnalyticsServicedispatches to the platform integration service for YouTube, TikTok, Instagram, Twitter, or Pinterest metrics.- The returned provider metrics are upserted into
postAnalyticsby(postId, platform, date). PostAnalyticsService.getPostAnalyticsSummaryexposes the latest per-post dashboard summary.PerformanceSummaryServicereadspostAnalyticsfor weekly top/worst content, platform performance, posting times, top hooks, and week-over-week trend.AnalyticsSyncService.syncAnalyticscopiespostAnalyticsrows intocontentPerformancefor the closed-loop optimization and brand-memory path.
Current-day provider metrics are available immediately in the publication analytics summary. Content-performance summaries intentionally use complete historical windows, so current-day rows become eligible once they are no longer today’s incomplete provider data.
Dataflow
The separate business analytics path aggregates monetization and usage data for operator-facing dashboards:
Representative V1 Smoke Path
The narrow business-analytics verification path for v1 is in:
The smoke test proves that representative aggregate inputs are converted into a complete dashboard response with:
- revenue totals
- credit sold/consumed totals
- ingredient totals
- leaderboards
- projections
- comparisons
That is enough to show the ingestion-to-dashboard contract is still wired without adding a large end-to-end analytics harness.
The provider-ingestion smoke path for v1 is in:
It verifies the representative media analytics path:
Run it with:
cd apps/server/api && TZ=UTC bunx vitest run --config vitest.config.ts src/collections/content-performance/services/analytics-ingestion-dashboard-smoke.spec.tsThe content-performance dashboard intentionally excludes current-day metrics because today’s provider data is incomplete. The smoke path records a provider ingestion row, verifies the publication analytics summary immediately, then advances the date window and verifies the same row appears in the weekly content-performance summary.
V1 Boundary
This page documents the active analytics backbone and its current split. It does not settle the full OSS-vs-EE extraction decision by itself; it gives the v1 release a concrete, inspectable surface first.