This chapter turns the "analytics insights" pillar of an AI-native CMS into a buildable system. We design the event pipeline (capture → transport → warehouse → serve), choose a privacy-first analytics layer (PostHog, Plausible, Umami, or GA4) against concrete 2026 pricing and trade-offs, stand up a warehouse and dashboards, and — the distinctly AI-native part — generate insight narratives and content-decay signals over your own data, then close the loop by feeding those signals back into the authoring and AI pipeline. The goal is not a vanity dashboard but a feedback organ: the CMS should know which content is decaying, why, and what to draft next.
An analytics build for a CMS is a pipeline with six stages. Keeping them decoupled is what lets you swap any single component (e.g. replace GA4 with Plausible, or DuckDB with ClickHouse) without rewriting the rest.
| Stage | Job | Stack-agnostic options (2026) |
|---|---|---|
| Capture | Emit events from web/app/server/AI crawlers | Client SDK (PostHog-js, Plausible/Umami <script>), server-side events, edge functions, log lines |
| Transport | Move events reliably, batch, buffer | Direct API ingest, Kafka/Redpanda, Vector/Fluent Bit, CDP (Segment/RudderStack/Jitsu) |
| Store | Durable, queryable event store | ClickHouse, DuckDB/MotherDuck, BigQuery, Snowflake, Postgres+TimescaleDB |
| Model | Turn raw events into metrics + a semantic layer | dbt models, dbt/Cube semantic layer, materialized views |
| Serve | Dashboards, alerts, APIs | Metabase, Lightdash, Evidence.dev, Grafana, the analytics tool's own UI |
| Act | Insight narratives + decay signals → authoring/AI pipeline | LLM summarizer, scheduled jobs, webhooks into the CMS/agent |
The first four stages are commodity data engineering; the last two — and especially — are where an AI-native CMS earns the "intelligence" label.
These four products are not interchangeable. GA4 and Plausible/Umami answer "what's happening on my site"; PostHog answers "what are users doing in my product." Pick by job-to-be-done, not by feature count.
| Tool | Category | Pricing (2026) | Self-host | Cookies / consent | Best for |
|---|---|---|---|---|---|
| GA4 | Marketing/attribution | Free to 10M events/mo; data on Google US servers | No | Cookies by default → consent banner in EU; data sampling | Attribution, Google Ads integration; you accept the privacy cost |
| Plausible | Privacy web analytics | Cloud ~$9/mo (10k pageviews) → ~$90/mo (1M); CE (AGPL) free self-host | Yes (needs Postgres + ClickHouse) | Cookieless, no banner under GDPR/CCPA/PECR; EU (Germany) hosting | Blogs/marketing sites wanting clean, curated metrics |
| Umami | Privacy web analytics | Cloud free tier 1M events/mo; self-host (MIT) free, no event limit | Yes (Postgres or MySQL) | Cookieless, no banner; ~2 KB script | Same niche as Plausible, but funnels/retention/journeys included free |
| PostHog | Product analytics suite | Free: 1M events + 5k session recordings/mo; then ~$0.00005/event (down to ~$0.000009 at 250M+); replay ~$0.005/recording stepping down | Yes (community edition, ClickHouse-backed, no limits) | Cookieless mode available; heavier ~50 KB+ SDK | Engineering-heavy teams wanting analytics + replay + flags + A/B + surveys + error tracking in one |
Decisions for a CMS specifically:
A common 2026 pattern: Plausible/Umami for the public-facing privacy-clean numbers, plus the warehouse export (PostHog → ClickHouse, or GA4 → BigQuery) for the deep modeling and AI narratives. Privacy-first does not mean data-poor; it means consent-free collection of non-PII event data you can still warehouse and reason over.
Event schema first. Before choosing storage, define a flat, append-only event table. A workable CMS event looks like:
event_id, timestamp, event_name, content_id, content_slug, content_type,
url, referrer, referrer_class (search|ai|social|direct|internal),
session_id (rotating daily salt, no PII), country, device, utm_*,
scroll_depth, time_on_page, is_bot, bot_name, props (JSON)
Two CMS-specific fields earn their keep: referrer_class with an explicit ai bucket (see §7), and bot_name so you can separate AI-crawler hits from human reads. Keep PII out by design — hash or rotate session IDs daily so you never store a stable cross-day identifier, which is what keeps you out of consent-banner territory.
Transport. For a CMS you rarely need Kafka on day one. Three tiers:
Storage — the 2026 OLAP picture. The "OLAP renaissance" gives you genuinely good open options:
| Store | Sweet spot for a CMS | Notes (2026) |
|---|---|---|
| DuckDB / MotherDuck | Single-node, up to ~hundreds of millions of rows; cheapest to operate | DuckDB crossed ~1M weekly PyPI downloads in 2025; ideal for interactive analysis and the LLM-narrative job; MotherDuck adds managed cloud |
| ClickHouse | High-volume event streams, billions of rows, real-time | Powers PostHog and Plausible internally; Fivetran connector + BigQuery→ClickHouse Dataflow template added in 2025 |
| BigQuery | You already have GA4 export; serverless, decoupled storage/compute | Continuous Queries now run against incoming data; pay-per-query |
| Postgres + TimescaleDB | You want one database for app + analytics | Simplest ops; fine to low-millions of events |
A clean, low-cost default for most CMS builds: collect via Umami/PostHog, export to a warehouse, and use DuckDB as the analysis engine — heavy ClickHouse for the billions-of-rows tier, DuckDB for the interactive thousands-to-millions the AI narratives actually read. The "ClickHouse for heavy lifting, DuckDB for light interactive" split is a recognized pattern.
The single biggest mistake in an analytics build is letting every dashboard re-define "active reader" or "engaged session" in its own SQL. Define metrics once in a semantic layer so dashboards, alerts, and the LLM narrative all compute the same number.
staging → marts models, define metrics in YAML, and ship tests so a broken metric fails CI rather than silently lying.The payoff for AI-native: when your LLM generates SQL or narratives, it queries the semantic layer, not raw tables. That is what makes text-to-SQL trustworthy — the model picks from governed metrics instead of hallucinating joins.
| Tool | Model | Strength | Use it when |
|---|---|---|---|
| Metabase | Visual query builder | 60k+ orgs; non-technical self-serve, no SQL | Editors/marketers need to click through content metrics |
| Lightdash | dbt-native semantic layer | Metrics governed in YAML; agentic BI | You run dbt and want one source of metric truth |
| Evidence.dev | Code-first (Markdown + SQL) | Version-controlled, narrative reports that read like documents | Engineers want git-tracked, reproducible content reports |
| Grafana | Time-series first | Real-time ops dashboards, alerting | Monitoring ingestion health and traffic anomalies |
| Tool-native UI | Built-in | Zero setup | Early stage; PostHog/Umami dashboards are enough |
For a CMS, Metabase for editors + Evidence.dev for the engineering content report is a strong, cheap combination. Evidence in particular fits the AI-native story: a content report is itself content — Markdown with embedded SQL and charts, regenerated on a schedule, and a natural place to embed LLM-written narrative.
This is the pillar realized. A raw dashboard requires a human to interpret it; an AI-native CMS writes the interpretation. Per Gartner (2025), 59% of enterprises now use some form of AI analytics, up from 33% in 2022 — narratives are the mainstream surface for it.
The narrative job, decomposed:
Architecture rules that keep narratives honest:
source_query it summarized. No free-floating percentages.A 2026 CMS analytics build that ignores AI traffic is missing the most important trend in content distribution. Two distinct flows:
(a) AI crawlers (bots reading your content). Cloudflare data (May 2026) shows training is the single largest purpose of AI crawling at ~52.5% of requests, and only ~8.4% of AI bot traffic is search — the only category that can send a referral. Crawl-to-referral ratios are brutal: ClaudeBot ~13,528 pages crawled per human visit, OpenAI ~1,252:1, Perplexity ~95:1, vs Googlebot ~5:1. Implication for your pipeline: classify bot_name (GPTBot, ClaudeBot, PerplexityBot, Google-Extended) and report crawl volume separately from human reads, so AI crawling doesn't inflate your engagement numbers.
(b) AI referrals (humans arriving from an AI answer). Add a referrer_class = ai bucket keyed on referrers like chatgpt.com, perplexity.ai, gemini.google.com, copilot.microsoft.com. These convert poorly: a SparkToro study (Jan 2026) found only 12-18% of Perplexity citations produce click-through, so 82-88% of citations send nobody. Track citations and visibility, not just clicks. Specialized monitors (Otterly.AI, Scrunch AI, SEOcrawl AI) watch whether your content is cited in AI Overviews / ChatGPT / Perplexity even when no click results — a leading indicator your decay model should incorporate, because organic CTR for informational queries with AI Overviews fell ~61% from June 2024 to Sept 2025.
The llms.txt standard (covered in this report's distribution chapters) is the supply side; this chapter is the measurement side — instrument both the crawlers reading your llms.txt and the referrals coming back.
This is the most CMS-specific analytics output and the cleanest place to close the loop.
Define decay as a multi-signal pattern, not a single drop. Industry guidance: a 20-40% drop in organic clicks over 8-12 weeks is confirmed decay needing intervention. But because AI Overviews now cannibalize clicks even at stable rankings, single-metric monitoring under-counts. Track signals moving together:
| Signal | Source | Decay direction |
|---|---|---|
| Organic clicks / impressions / CTR / avg position | Google Search Console API | clicks ↓ while position holds → AI cannibalization |
| Page sessions & engaged time | Your analytics (Umami/PostHog) | falling engagement |
| Internal-link CTR | Event pipeline | losing internal authority |
| Statistic age | CMS metadata (last data-year cited) | a 2023 stat in 2026 = stale signal to readers and engines |
| Last-updated date | CMS field | freshness gap |
| AI-citation presence | Otterly/Scrunch-style monitor | dropped from AI answers |
Build a decay score. Materialize a content_health table (per content_id) that joins GSC + analytics + CMS metadata, computes an 8-12-week trend per signal, and emits a weighted score plus a recommended action: refresh / consolidate / delete / leave. AI tooling here flags pages early — when impressions, clicks, CTR and position start moving together but before traffic fully collapses. Run it weekly; the cost of a DuckDB query over a few hundred thousand rows is negligible.
The action taxonomy (from refresh frameworks): Update (replace outdated stats, add sections, refresh metadata) is the default; Consolidate when two posts compete; Delete/redirect thin or duplicate pages; Leave seasonal or evergreen pages whose decline is expected.
The analytics build is only "intelligence" if its output changes what gets written. Three loop integrations, in increasing autonomy:
Editor-facing (human-in-the-loop, default). The decay score and the LLM narrative surface inside the CMS as a "Content Health" panel on each entry: score, the specific decaying signals, the AI-suggested action, and a "Draft refresh" button. The editor decides. This is the safe, recommended baseline.
Draft generation (agent-assisted). When a page is flagged "refresh," a job assembles a refresh brief — the stale statistics, the queries now winning impressions, the AI-answer gaps where you're cited-but-not-clicked — and hands it to the authoring AI (see the report's authoring-pipeline chapters) to produce a draft update against your content model. The draft enters the normal review workflow; nothing publishes unattended.
Topic discovery (strategic). Aggregate rising queries, AI-referral landing patterns, and zero-click-but-cited topics into a backlog of new content the AI pipeline can draft. The same warehouse that measures decay now drives the editorial calendar.
Guardrails for the loop: keep a human approval gate before publish (matching the report's broader human-in-the-loop posture); log every AI-suggested action and its outcome so the decay model can learn which refreshes actually recovered traffic; and never let the AI both measure success and decide to publish without an independent check — separation of "judge" and "author" prevents the system from optimizing for its own metric. Feed recovered-vs-not outcomes back as labels to tune the decay weights over time.
referrer_class=ai bucket and bot_name, and keep PII out by rotating session IDs daily — that's what keeps you consent-banner-free.ai referral bucket; crawler vs referral distinction.