Skip to main content

Configuration

Analytics

First-party page-view / search / feedback / assistant events captured by the built-in beacon. Independent of GA4 / PostHog / Plausible integrations.

NookDocs ships a first-party analytics beacon that writes anonymous events directly to the platform's own Supabase tables. The data powers the per-project Analytics dashboard and feeds the feedback backlog.

This is independent of third-party integrations — GA4, PostHog, Plausible, Mixpanel, and the dozen other providers in the integrations.* block continue to run regardless of whether the first-party beacon is on.

What's captured

Event typeEmitted byMetadata
viewEvery docs page loadreferrer, loadTime
searchSearch modal submissionquery, resultCount
feedback_up / feedback_downThe Feedback widgetreason, hasComment
assistant_open / assistant_messageThe AI chat bubblequeryLength, responseTime
llm_exportAn LLM/agent fetching llms.txt, llms-full.txt, or a page's .mdkind

Every event records:

  • session_id — anonymous per-tab random string, lives in sessionStorage. Resets on browser close. No cookies, no localStorage, no cross-tab dedupe.

  • user_agent (truncated to 512 chars) — server-side classified into is_bot + bot_family (gpt | claude | perplexity | google | bing | other), and into browser / OS / device for the tech breakdowns.

  • referrer (from the browser's document.referrer).

  • path — the normalized docs path (no host, no query string).

  • Approximate locationcountry, and (going forward) region + city — derived from your visitor's connection at the edge. Country is available historically; region and city are captured for new visits only.

  • UTM tagsutm_source / utm_medium / utm_campaign / utm_term / utm_content, read from the landing URL's query string when present. Powers the Channel / Campaign breakdowns. Captured going forward.

No IPs, no emails, no user identifiers.

Bot detection

The ingestion endpoint classifies the user-agent into a bot family so the dashboard can split AI-mediated traffic from human traffic. Matched patterns:

  • gptGPTBot, ChatGPT-User, OAI-SearchBot

  • claudeClaudeBot, Claude-Web, anthropic-ai

  • perplexityPerplexityBot, Perplexity-User

  • googleGooglebot, Google-Extended, GoogleOther, Storebot-Google

  • bingbingbot, BingPreview, msnbot

  • other — generic bot, crawler, spider, HeadlessChrome, curl/, wget/

The Google-Extended token is the AI-training opt-out UA — any scraper asking for its content still shows up in the AI column.

Dashboard

Switch the date preset, then click a country row to cross-filter every panel

Dashboard → your project → sidebar → Analytics

The page renders in a single fetch and updates when the toolbar changes:

  • Date range — a preset menu (Now, Today, Yesterday, Last 24 hours, Last 7 / 30 days, Last 12 months, Week / Month / Year to date, All time) plus Custom for an arbitrary start–end range via a two-month calendar. The ‹ › arrows step the window back/forward by its own length. Ranges resolve in your local timezone, so "Today" means your midnight.

  • Now is the live view: a rolling 30-minute window, bucketed per minute, auto-refreshing. It has no granularity selector (the grain is fixed).

  • Granularity — for every range except Now, bucket the Traffic chart by Hourly / Daily / Weekly / Monthly. Options are constrained to grains that fit the window, so you never plot thousands of hourly points or a single monthly bar: short windows offer hourly, long windows offer weekly/monthly.

    WindowAvailable grains
    Today · Last 24 hoursHourly
    Last 7 days · Week to dateHourly · Daily
    Last 30 days · Month to dateDaily · Weekly
    up to ~4 monthsDaily · Weekly · Monthly
    Last 12 months · Year to date · All timeWeekly · Monthly
  • Hide bot traffic (default on) — Strips UA-classified crawlers from view / visitor totals. The AI-vs-human split panel always shows both sides regardless of this toggle.

Panels

PanelWhat it shows
Views tileTotal view events in the window.
Unique visitors tileDistinct session_ids seen in the window.
Searches tileTotal search events + the top query label.
Feedback tileThumbs-up count + thumbs-down count.
Traffic chartArea (views) + unique-visitor line, bucketed by the chosen granularity. Empty buckets show as zero, not gaps.
LocationsTabbed: Map (world choropleth shaded by visitors, hover for the count) · Country · Region · City — each a ranked visitor list.
TechTabbed: Browser · OS · Device — ranked visitor list per client, with real brand logos.
Traffic sourcesTabbed: Channel (Direct / Organic Search / Social / AI / Referral / Campaign…) · Referrer (referring site, with favicon) · Campaign (UTM-tagged visits) · Keyword (utm_term).
AI-vs-human splitStacked horizontal bar: Human + ChatGPT + Claude + Perplexity + Google + Bing + Other bots. Per-family counts + percentages in the legend.
AI & LLMsCrawl views, LLM exports, Ask-AI usage, and AI referrals at a glance, plus tabs for Crawlers (by bot), AI pages (what bots read most), and Exports (llms.txt / llms-full.txt / page .md fetches).
Top pagesSame ranked-list card with a Views / Unique metric toggle — top 10.
Top searchesQuery / count / avg result count — top 10.
Recent feedbackLast 20 page_feedback rows with rating, reason, comment, and path.

Every breakdown list has a sort toggle (highest-first by default) — Visitors on most cards, Views / Unique on Top pages.

Drill-down filters

Click any breakdown row — a page, browser, OS, device, country, region, city, referrer, channel, or keyword — to filter the entire dashboard by it. Filters stack (AND): e.g. Country = Germany and Browser = Firefox. A hover filter icon marks clickable rows. The always-visible Filters control (next to the date range) shows how many are active and lets you remove them individually or Clear all. Totals, the traffic chart, and every breakdown all respect the active filters.

Campaign tracking (UTM)

Tag the links that point at your docs so the Traffic sources → Campaign / Channel breakdowns can attribute the visits:

https://docs.example.com/quickstart?utm_source=newsletter&utm_medium=email&utm_campaign=launch
  • utm_source, utm_medium, utm_campaign, utm_term, utm_content are all picked up from the landing URL.

  • The medium drives the Channel: cpc/ppc/paidPaid Search, email/newsletterEmail, otherwise → Campaign.

  • Tags are read from the landing page (the first page opened with them) and recorded going forward — past visits aren't backfilled.

AI & LLM insights

NookDocs is built for the LLM era, so the dashboard surfaces how AI actually uses your docs — not just human pageviews:

  • Crawlers — views from AI/search bots, grouped by family (ChatGPT/GPT, Claude, Perplexity, Google, Bing, other). Same classification as the AI-vs-human split.

  • AI pages — which pages those bots read most, so you can see what AI is learning from your docs.

  • Exports — how often LLMs/agents fetch your machine-readable surfaces: llms.txt, llms-full.txt, and per-page Markdown (/<page>.md). Each fetch is counted.

  • AI referrals — humans who arrived from an AI assistant. Visits carrying an AI source (e.g. ChatGPT appends ?utm_source=chatgpt.com) or an AI referrer are classified as the AI channel in Traffic sources, not lumped into Campaign/Referral.

  • Ask AI — opens and questions on your docs' built-in assistant.

All of these respect the active drill-down filters and date range.

Data latency

Events land in page_events within ~1 second of a page view (edge ingestion). The dashboard reads directly from live tables — no materialized-view rebuild to wait for. For tenants with high volume, per-day rollups (materialized views) are on the roadmap.

Configure from the dashboard

Dashboard → your project → Editor → Configurations → AI & Advanced → Analytics

One toggle: First-party beacon (default on). Turning it off disables first-party event capture entirely for this tenant — useful for regulated industries or readers who want to rely on their own analytics stack.

Config reference

analytics.enabledbooleandefault: true

Enable the first-party beacon. When false, no events are written to page_events / visitors and the Analytics dashboard shows "No data" for this project.

Example

{
  "analytics": {
    "enabled": false
  }
}

API

Ingestion

POST /api/v1/projects/:projectId/events — public ingestion.

{
  "event_type": "view",
  "path": "/guides/auth",
  "session_id": "abc123…",
  "metadata": { "referrer": "https://google.com/", "loadTime": 142 }
}

Returns 202 Accepted on success, 400 on malformed input, 404 on unknown project. Payload caps: 500-char path, 2 KB metadata JSON. CORS open — the endpoint accepts cross-origin POSTs from any tenant subdomain.

Summary (dashboard-only)

GET /api/v1/projects/:projectId/analytics/summary?from=…&to=…&unit=day&tz=Europe/Istanbul&bots=excluded — dashboard read.

Requires a JWT or API-key Authorization: Bearer … header. Returns a single JSON blob with totals, timeseries, aiSplit, topPages, topSearches, geo (countries / regions / cities), tech (browsers / os / devices), sources (channels / referrers / campaigns / keywords), llm (usage / crawlers / pages / exports), and recentFeedback — everything the Analytics page renders. Query params:

  • from, to — ISO timestamps bounding the window [from, to). Default: last 30 days.

  • unit — timeseries bucket grain: hour | day | week | month (default day).

  • tz — IANA timezone for bucket boundaries (default UTC). The dashboard sends the viewer's zone.

  • botsexcluded (default) | included. When excluded, view / visitor totals and breakdowns skip UA-classified bots.

Retention

  • Raw events (page_events) — 90 days. The platform runs prune_old_page_events() on a schedule.

  • Daily aggregates — summary rollups (planned) retain indefinitely.

  • Visitors (visitors) — no time cap; the composite (project_id, session_id) PK dedupes sessions across reloads.

Privacy stance

The beacon does NOT record:

  • IP addresses. Approximate country / region / city come from coarse, pre-resolved geolocation at the edge — the raw IP is never read or stored.

  • Cookies or localStorage identifiers.

  • Query strings beyond the normalized path.

  • Session state that persists across browser restarts (sessionStorage clears on close).

Do Not Track is honored via the analytics.enabled toggle — tenants that need wholesale opt-out flip it off and ship.

Was this page helpful?

Last updated August 7, 2026