Last updated 2026-04-26

Analytics dashboard

/ops/analytics is the OtiumWork-owner dashboard for understanding the SaaS itself: who's visiting, where they came from, what's converting, and how the public pages score for SEO + GEO.

It is owner-only (allowlisted by OPS_ACCESS_EMAILS / PRODUCT_OWNER_EMAIL env vars). No customer admin sees it.

What gets logged

Every non-API GET request lands a row in the pageview table via a before_request hook:

  • path, referer, user-agent, anonymized IP (SHA-256), session cookie hash
  • UTM parameters from the URL (utm_source, utm_medium, utm_campaign, utm_content, utm_term)
  • is_bot flag — UA-pattern based, used to filter the dashboard by default
  • pageview_token — a short random string the page renders so the engagement beacon can update the row later
  • engagement_seconds + scroll_pct — set by the engagement beacon on each page

Excluded paths: /static/*, /api/*, /sw.js, /favicon*, /robots.txt, /health*, and /ops/* (so the dashboard doesn't pollute its own data).

Engagement beacon

A small JS snippet runs on landing.html, base.html (every authed page), and pricing.html. It:

  1. Sends a heartbeat every 15 seconds with the visible time + max scroll percent
  2. Sends a final flush via navigator.sendBeacon() on pagehide / beforeunload
  3. POSTs to /api/pv/beacon with the token + engagement seconds + scroll %

Server updates the matching pageview row's engagement_seconds (max value wins, capped at 24h) and scroll_pct (max value wins, capped at 100).

Sections on the dashboard

  • Headline KPIs: pageviews 24h / 7d / 30d, sessions 7d / 30d, signups 30d
  • Pageviews chart: 30-day sparkline (CSS bars, no JS lib)
  • Top pages: views + sessions + avg time + scroll %
  • Top external referrers: organic / direct / social breakdown
  • UTM breakdown: per (source, medium, campaign) combo — fills in once you tag PPC + email links
  • Signup funnel: /signup visits → completed signups + conversion %
  • Top signup sources (90d): UTM source captured at signup time on the company row
  • Platform snapshot: companies registered, paying via Stripe, free→paid rate, bot share
  • AI assistant traffic: classifies known AI-host referers (ChatGPT, Perplexity, Claude, Gemini, Copilot, Meta AI, You.com)
  • SEO audit: per public page — title length, meta description, H1 count, canonical, OG tags, JSON-LD, word count → score 0-100
  • GSC organic queries: real search keywords from Google Search Console (when GSC integration is configured)

GSC integration

Modern search engines strip the search query from HTTPS Referer headers, so the only way to see real organic keywords is the GSC API. To enable:

  1. Verify your site at https://search.google.com/search-console.
  2. Create a service account at https://console.cloud.google.com → IAM & Admin → Service Accounts → Create.
  3. Enable the Google Search Console API in the project (APIs & Services → Library → search "Search Console API" → Enable).
  4. Create a JSON key for the service account → downloads a .json file.
  5. In Search Console → your property → Settings → Users and permissions → add the service account email (*@*.iam.gserviceaccount.com) as a Restricted user.
  6. Paste the JSON content into /ops/integrationsGSC service account JSON field.
  7. Set the GSC property URL field — exactly as it appears in Search Console: - For Domain properties: sc-domain:otiumwork.com - For URL-prefix: https://otiumwork.com/
  8. POST to /api/cron/gsc-sync with the X-OtiumWork-Cron-Token header — schedule a daily cron for it.

Data appears on the dashboard within ~24-48h of GSC verification (Google needs time to collect query data).

Privacy

  • IPs are SHA-256 hashed with a salt (PAGEVIEW_IP_SALT env var) before storage. We never store raw IPs.
  • Session IDs are random 16-byte tokens stored in a session cookie — no PII.
  • 13-month retention on pageviews (matches GA4 default).
  • See Privacy Policy.

Permissions

Owner-only — strictly limited to the email(s) in OPS_ACCESS_EMAILS env var. Customer admins (Leonid at SoftInWay, Mariya at LelekaArt, etc.) cannot see this page or the data behind it.


See something wrong or outdated in this article? Report it →