# Pulse Tower > Privacy-first analytics dashboard combining pageviews, revenue, and GitHub activity in one place. ## About Pulse Tower is a developer analytics dashboard that tracks website pageviews (no cookies, no IP storage), Stripe revenue, and GitHub code activity in a single real-time view. It features AI-powered chat for querying analytics data in plain English, smart filtering, and public shareable dashboards. ## Documentation - [Install the Script](https://pulsetower.ai/docs/embed): Add the analytics embed script to your site - [AI Chat](https://pulsetower.ai/docs/ai-chat): Ask questions about your analytics in plain English - [Event Tracking](https://pulsetower.ai/docs/events): Track custom events, clicks, and form submissions - [Connect Stripe](https://pulsetower.ai/docs/stripe): See MRR and revenue data - [Connect GitHub](https://pulsetower.ai/docs/github): Track commits and PR activity - [Invite Your Team](https://pulsetower.ai/docs/team): Share dashboards with teammates - [Privacy & Data](https://pulsetower.ai/docs/privacy): What we collect and what we don't --- ## Install the Script Add one script tag to your site. Under 2KB. No cookies. Takes two minutes. ### Step 1: Sign Up and Create a Site Create an account at https://pulsetower.ai/signup, then add your site in the dashboard. You'll get a unique site token. ### Step 2: Add the Script Replace YOUR_SITE_TOKEN with the token from your dashboard. ```html ``` ### Step 3: See Your Data Pageviews show up in your dashboard in real-time. No setup delay, no waiting for "yesterday's data." ### FAQ **Does this add cookies to my site?** No. Pulse Tower uses zero cookies, zero IP storage, and zero fingerprinting. No cookie banner needed. **How big is the script?** Under 2KB. It uses sendBeacon so it doesn't block page load. **Can I use this with a single-page app?** Yes. The script patches pushState/replaceState for SPA support. Route changes are tracked automatically. For hash-based routers, add `data-hash="true"`. ### Script Configuration All behavior is controlled via `data-*` attributes on the script tag: | Attribute | Purpose | Default | | --- | --- | --- | | data-site | Site token (required) | — | | data-endpoint | Custom API endpoint for proxying | Auto | | data-domain | Root domain for subdomain tracking | Auto | | data-allowed-domains | Comma-separated domains for cross-domain tracking | None | | data-allow-localhost | Track on localhost for dev testing | false | | data-hash | Track URL hash changes as pageviews | false | | data-exclude-paths | Comma-separated paths to ignore (supports * wildcard) | None | | data-no-spa | Disable SPA route change detection | false | ### Excluding Traffic Three methods to exclude unwanted visits: 1. **Path exclusion**: `data-exclude-paths="/admin/*,/internal/*"` 2. **Self-exclusion**: Run `localStorage.pulse_ignore = "true"` in browser console 3. **IP blocking**: Add CIDR rules in site Settings tab ### Cross-Domain Tracking Track visitors across separate root domains by adding `data-allowed-domains`. The script passes a visitor ID via URL params automatically. Install the same script with the same token on both domains. ### Scroll & Click Goals Track element visibility and clicks without JavaScript: - `data-pulse-scroll="goal_name"` — fires event when element scrolls into view - `data-pulse-scroll-threshold="0.8"` — visibility ratio (default 0.5) - `data-pulse-scroll-delay="2000"` — ms to wait before firing - `data-pulse-goal="click_name"` — fires event on click - `data-pulse-goal-*` — custom properties on click goals --- ## AI Chat Ask questions about your analytics in plain English. Get answers powered by your real data. ### How It Works 1. **Open the Chat Panel** — Click the sparkle icon in the top bar from any page. Press Esc to close it. 2. **Choose Your Scope** — Use the site selector to pick a specific site or "All Sites" to query across your workspace. 3. **Ask Anything** — Type a question in plain English. The AI fetches real data from your analytics, referrers, devices, UTM campaigns, events, and goals. ### Example Questions - What are my top pages this week? - Where is my traffic coming from? - Which site gets the most visitors? - How do pageviews compare to last month? - What devices do my visitors use? - What events are being tracked? - Show me UTM campaign performance - What are my goal conversion rates? ### Chat History Conversations are saved automatically. Each chat thread is private to your account within your workspace. Click + to start a fresh conversation, or the chat icon to see previous threads. ### FAQ **Does the AI make up numbers?** No. Every answer is backed by real tool calls to your analytics data. If there's no data, it says so. **Can other team members see my chats?** No. Chat threads are private to your account. **What data can the AI access?** Pageviews, daily stats, top pages, referrers, UTM campaigns, device/browser breakdowns, custom events, and goal conversions. --- ## Event Tracking Track custom events and user interactions. No extra setup needed for common events. ### Auto-Tracked Events These events are captured automatically by the embed script: - **outbound_click** — Fires when a visitor clicks a link to an external domain. Properties: url - **file_download** — Fires when a visitor clicks a downloadable file (.pdf, .zip, .csv, .xlsx, .doc, .dmg, .exe). Properties: url, fileType - **email_click** — Fires when a visitor clicks a mailto: link. Email address is NOT captured for privacy. ### Custom Events Track any interaction with one line of JavaScript. The `pulse.track()` function is available globally after the embed script loads. ```javascript // Track a simple event pulse.track("signup_click"); // Track with properties pulse.track("plan_selected", { plan: "pro", interval: "annual" }); ``` #### Button click example ```html ``` #### React / Next.js example ```typescript declare global { interface Window { pulse?: { track: (name: string, props?: Record) => void }; } } function SignupButton() { return ( ); } ``` #### Form submission example ```javascript document.querySelector("form").addEventListener("submit", () => { pulse.track("form_submit", { formId: "contact" }); }); ``` ### API Reference ``` pulse.track(name: string, properties?: object) ``` - **name** — Required. The event name. Use snake_case. Keep it short and descriptive. - **properties** — Optional. A flat object of key-value pairs. Values should be strings or numbers. Avoid nested objects. ### Viewing Events Go to your site dashboard and click the Events tab to see every event name with its total count for the selected date range. ### FAQ **Do events count against pageviews?** No. Events and pageviews are tracked separately. **Is there a limit on event names?** No hard limit. Keep the total number of distinct event names manageable (under 50 is ideal). **Are event properties stored securely?** Yes. Don't include sensitive user data like emails or passwords in properties. **Do auto-tracked events work with SPAs?** Yes. Outbound clicks and file downloads work regardless of routing. --- ## Connect Stripe See MRR, daily revenue, and recent charges in your dashboard. ### Step 1: Create a Restricted Key Click the pre-filled link in Settings to open Stripe with the exact permissions ready. Or create manually in Stripe Dashboard > Developers > API Keys > Restricted Keys: | Resource | Permission | | --- | --- | | Charges | Read | | Balance | Read | | Everything else | None | Never use your secret key. A restricted key limits what Pulse Tower can access. ### Step 2: Paste It in Site Settings Open your site in the dashboard, click the Settings tab, and paste your restricted key. Each site has its own Stripe key — you can connect different Stripe accounts to different sites. ### Step 3: See Revenue Data Revenue data appears on your site dashboard immediately. You'll see daily revenue, MRR, and recent charges. ### Security FAQ **How is my key stored?** Your Stripe key is encrypted with AES-256-GCM before it touches the database. It never appears in client-side code or logs. **Can Pulse Tower charge my customers?** No. With a read-only restricted key, Pulse Tower can only read charge and balance data. **Can I revoke the key?** Yes. Delete the restricted key in Stripe at any time and Pulse Tower will stop showing revenue data. --- ## Connect GitHub Track commits, PRs, and repo activity alongside your traffic and revenue. ### Step 1: Authorize GitHub Go to Dashboard > Settings > Integrations and click Connect GitHub. You'll be redirected to GitHub to authorize Pulse Tower. ### Step 2: Grant Read Access Pulse Tower requests read-only access: | Scope | What We Read | | --- | --- | | Repos | Commit count, recent activity | | Pull Requests | Open/closed count | | Profile | Username (for display) | ### Step 3: See Activity Data Once connected, your site dashboard shows a GitHub activity chart with weekly commit history. Commit markers also appear as dashed lines on the main traffic chart so you can correlate deploys with traffic changes. ### FAQ **Can I disconnect GitHub later?** Yes. Revoke access from GitHub Settings > Applications at any time. **Does this work with private repos?** Yes. Pulse Tower only reads aggregate activity data — never code contents. **How is my GitHub token stored?** Your OAuth token is encrypted with AES-256-GCM before storage, just like Stripe keys. --- ## Invite Your Team Share dashboards with your team. No per-seat pricing. ### How Workspaces Work Every account gets a personal workspace automatically. Invite teammates to share the same dashboard, sites, and integrations. Everyone sees the same real-time data. ### Option 1: Invite by Email Go to Dashboard > Settings > Team Members. Enter a teammate's email and click Invite. ### Option 2: Share a Link Click "Copy Invite Link" to get a shareable URL. Links expire after 7 days. ### What Teammates See All workspace members see: all sites and analytics, revenue data (if Stripe connected), GitHub activity (if connected), and team member list. ### FAQ **Is there a limit on team members?** No. Invite as many people as you want. No per-seat charges. **Can I remove a team member?** Coming soon. Contact us if you need to remove someone. --- ## Privacy & Data What we collect, what we don't, and how your data is handled. ### What We Collect When someone visits a site with the Pulse Tower embed script: | Data | Example | | --- | --- | | Page path | /blog/hello-world | | Referrer | google.com | | Device type | mobile, desktop, tablet | | Browser | chrome, safari | | Screen width bucket | sm, md, lg | | Country | US, GB (inferred from timezone, not IP) | That's it. All data is aggregate and anonymous. Visitors can opt out by setting `localStorage.pulse_ignore = "true"` in their browser console. ### What We Don't Collect - No cookies of any kind - No IP addresses (never stored, hashed, or logged) - No fingerprinting (no canvas, WebGL, or fingerprinting techniques) - No personal data (no user IDs, session IDs, or tracking pixels) - No cross-site tracking (each site is isolated) ### Do I Need a Cookie Banner? No. Pulse Tower does not use cookies or collect personal data, so no consent banner is required under GDPR, CCPA, or PECR. ### Integration Data When you connect Stripe or GitHub: - API keys and OAuth tokens are encrypted with AES-256-GCM before storage - Keys never appear in client-side code, URLs, or logs - Revenue and activity data is fetched on-demand and displayed only to workspace members - You can revoke access at any time --- ## Key Features - One script tag, under 2KB, no cookies, no IP storage - AI chat powered by real analytics queries (not hallucinated data) - Smart filtering by referrer, device, browser, UTM source - Public shareable dashboard URLs - GitHub integration for commit and PR tracking - Stripe integration for MRR and revenue tracking - Custom event tracking with pulse.track() API - Auto-tracked events: outbound clicks, file downloads, mailto clicks - SPA support (Next.js, React Router, etc.) - Real-time updates via Convex - Multiple workspaces and team collaboration - AES-256-GCM encrypted integration keys ## Technical Stack - Next.js (App Router) - Convex (real-time backend) - TypeScript - Tailwind CSS