# Blueprint – Tracking Documentation

**Author:** Whittfield Holmes  
**Last updated:** February 2026  
**Purpose:** Complete reference for the app’s dataLayer tracking: what’s tracked, when, and how to implement it in GTM/GA4.

---

## Table of contents

1. [Overview](#1-overview)
2. [Configuration](#2-configuration)
3. [Event catalog](#3-event-catalog)
4. [Data schema](#4-data-schema)
5. [Measurement framework](#5-measurement-framework)
6. [Implementation guide](#6-implementation-guide)

---

## 1. Overview

Blueprint uses a **dataLayer** pattern: the app pushes events to `window.dataLayer`. You can consume them via your own GTM/Launch setup, or use Blueprint’s built-in delivery to send hits to GA4.

### Delivery options

| Option | Description |
|--------|-------------|
| **dataLayer only** | Events go to `window.dataLayer`. You add GTM, gtag, or Adobe Launch and build triggers. |
| **Client-side (gtag.js)** | Blueprint loads gtag and sends events to GA4 from the browser. Set GA4 Measurement ID in Admin. |
| **Server-side (Measurement Protocol)** | Blueprint sends events to a Supabase Edge Function, which forwards them to GA4. API secret stays on the server. |
| **Both** | Events are sent via both client gtag and the server-side proxy. |

### Architecture (single dispatch — no double-counting)

The app **never** calls `gtag('event', ...)`. It only pushes **plain objects** to `window.dataLayer` (e.g. `{ event: 'blueprint_section_view', section: 'overview', ... }`). This avoids dual-dispatch: both a GTM tag and gtag.js processing the same push would otherwise send the same event twice to GA4.

```
Blueprint app → pushToDataLayer(eventName, params)
    ├→ window.dataLayer.push({ event: name, ... })  (always when enabled)
    ├→ (when delivery = client or both) app loads gtag.js for config/consent only; for virtual_page_view only, a second object { event: 'page_view', page_location, ... } is pushed
    └→ (when delivery = server or both) POST /functions/v1/ga4-measurement → GA4 mp/collect
```

**Choose one path to GA4:**

| Setup | What to do |
|-------|------------|
| **GTM is your TMS** | Set Admin → Tracking → Delivery to **dataLayer only**. Do not set a GA4 Measurement ID in Blueprint (or leave it for server-side only). Add the GTM container snippet in `<head>` or immediately after `<body>`. Create GA4 Event tags in GTM that fire on the custom event names (e.g. `blueprint_section_view`). The app will not load gtag.js for events, so only GTM sends to GA4. |
| **Client-side gtag (no GTM)** | Set Delivery to **Client-side (gtag.js)** and set the GA4 Measurement ID. The app will load gtag and push objects to dataLayer; gtag.js will process them. Do not add a separate GTM container that also fires GA4 tags on the same events, or you will double-count. |
| **Both GTM and client delivery** | Avoid. If you use both, you get two paths to GA4 (GTM tag + gtag processing dataLayer) and 2× event volume. |

### Requirements

- **Enable tracking:** Admin → Tracking → Enable dataLayer tracking
- **dataLayer only:** Add GTM, gtag, or Adobe Launch to your deployment
- **Client or Server:** Set GA4 Measurement ID in Admin → Tracking. For server-side, deploy the `ga4-measurement` Edge Function and set `GA4_API_SECRET` in Supabase secrets.

### Storage

- **Local:** `localStorage` under `blueprintDatalayerConfig` (enable, prefix, category toggles)
- **Server:** `app_settings` for `ga4_measurement_id` and `tracking_delivery` (Admin only)
- **Project panel (per project):** GA4 Measurement ID, GTM Container ID, Adobe credentials, and optional **pixel IDs** (Meta, LinkedIn, TikTok). Pixel IDs are for reference and client handoff only; **Blueprint app tracking does not use them** (no events or tags are fired from these IDs). Deploy client pixels via your own GTM/Launch setup.

### Admin exclusions

**Admin interactions are never tracked.** When the user is in the Admin section, or when an action targets Admin (e.g. clicking the Admin overview card), no events are pushed. This keeps admin behavior out of analytics.

---

## 2. Configuration

### Admin → Tracking settings

| Setting | Default | Description |
|--------|---------|-------------|
| **Enable dataLayer tracking** | Off | Master switch. When off, no events are pushed. |
| **Event name prefix** | `blueprint_` | Prepended to event names (e.g. `blueprint_section_view`). Alphanumeric + underscore only. |
| **Track buttons** | On | Fire `button_click` for button/link-like interactions |
| **Track forms** | On | Fire `form_submit` on form submit |
| **Track links** | On | Fire `outbound_click` for outbound links |
| **Track sections** | On | Fire `section_view` on section change and `overview_card_click` on overview cards |
| **Track downloads** | On | Fire `file_download` for script/deliverable exports |

### Default config (when not overridden)

```javascript
{
  enabled: false,
  prefix: 'blueprint_',
  track_buttons: true,
  track_forms: true,
  track_links: true,
  track_sections: true,
  track_downloads: true
}
```

### GA4 Admin (property G-XXXXXXXX)

| Topic | What to do |
|--------|------------|
| **User ID** | The app hashes the internal user ID (SHA-256) before sending so it is non-reversible and GA4 ToS/GDPR compliant. It sets hashed `user_id` in the GA4 config on sign-in and clears it on sign-out (via `setGa4UserId`). In GA4 Admin → Data display → **User data collection**, ensure "User ID" is enabled so GA4 can stitch authenticated sessions and build user-level funnels. |
| **Session anchor (initial page_view)** | The first dataLayer push is deferred by one tick; when it runs, the app pushes a **page_view** event first (with `page_location`, `page_title`, `page_referrer`, `language`, `session_id`) so GA4 has a session anchor before any content events. Event name is prefixed (e.g. `blueprint_page_view`). In GTM, add a GA4 Event tag that fires on this event and sends a GA4 `page_view` with those parameters so G-XXXXXXXX receives a proper session start. |
| **Virtual page views (SPA)** | Each SPA section change pushes `virtual_page_view` to the dataLayer and sends a GA4 `page_view` with `page_path`, `page_title`, and `page_location`. No extra GTM tag is required when using Blueprint’s client-side delivery. For dataLayer-only setups, add a GTM tag that fires on `virtual_page_view` (or `gtm.historyChange`) and sends a GA4 Event of type `page_view` with those parameters. |
| **Cross-domain linking** | If the app or linked sites send users to partner domains (e.g. payment, portals), configure **linked domains** in GA4 so the `_gl` linker parameter is appended to outbound links and sessions are preserved. In GA4 Admin → **Data Streams** → your Web stream → **Configure tag settings** → **Configure your domains**, add the partner domains. Deploy the cross-domain linker (Blueprint’s Cross Domain section or GTM tag) so links to those domains include `_gl`. Verify in the network tab or by clicking a link that the URL contains `_gl=1*...`. |
| **Session integrity** | Every event includes a client-generated `session_id` (e.g. `bp_1709876543210_abc12def`) so you can segment by logical page-load session when analyzing. If you see non-monotonic timestamps or multi-day gaps in a single export, treat the payload as multi-session and filter by `session_id` before analysis. |
| **GA4 client_id in dataLayer** | The app reads the GA4 `client_id` via `gtag('get', measurementId, 'client_id', callback)` once per page (after the first event push) and pushes it to the dataLayer as `ga4_client_id`. Create a GTM Data Layer Variable for `ga4_client_id` so you can use it in tags or for debugging. This supports user-level stitching between anonymous and authenticated sessions. **FPID / cross-subdomain** is only needed if you track across subdomains; for a single domain you do not need an FPID cookie. |
| **element_id** | Sent only when the element has an `id`; omitted when empty so GA4 custom dimensions are not polluted with blank values. |
| **Initial events / GTM** | The first dataLayer push is deferred by one tick (setTimeout 0) so GTM has time to initialize; subsequent events push immediately. Load the GTM container in `<head>` before app scripts so home/overview section views are captured with `gtm.uniqueEventId`. |
| **page_view on SPA routes** | The app pushes `virtual_page_view` and (when client delivery) `blueprint_page_view` with `page_location`, `page_path`, `page_title` on each section change. Add a GTM tag: trigger = Custom Event `blueprint_page_view` (or `gtm.historyChange-v2`), GA4 Event = `page_view` with those parameters so GA4 receives a page_view per route. |
| **Scroll depth** | Configure GTM Scroll Depth trigger with thresholds at 25%, 50%, 75%, and 90%. Map to a GA4 `scroll` event with `percent_scrolled` so content consumption is measurable. |
| **Consent default** | The page `<head>` runs `gtag('consent', 'default', { all denied })` before any GTM or analytics script so no data is collected until the user accepts (GDPR/ePrivacy). Only `consent` `update` runs after verified acceptance. |
| **Auth state** | On session init and on login/logout the app pushes `blueprint_auth_state` with `user_type` (`authenticated` or `anonymous`), `user_id` (SHA-256 hashed when signed in; never raw UUID), and `account_tier` (e.g. `free`, `pro`) so GA4 can segment authenticated vs anonymous users. |
| **Internal traffic** | To avoid inflating funnels with developer/QA sessions: (1) In GA4 Admin → **Data Streams** → your Web stream → **Configure tag settings** → **Define internal traffic**, add a rule by IP or by custom parameter. (2) Optionally tag internal sessions in the app: add `?internal=1` to the URL or set `localStorage.setItem('blueprint_internal_traffic', '1')` in the browser. When set, every event includes `traffic_type: 'internal'`. Create a GA4 internal traffic rule that matches this parameter and exclude those hits from key reports. |
| **GTM placement / early events** | The first app events (e.g. `blueprint_section_view` for home/overview) can fire before GTM has loaded if the container loads late. To capture them: (1) Place the GTM container snippet in `<head>` or immediately after the opening `<body>` tag so GTM initializes before or with the app. (2) In GA4 DebugView, confirm that `blueprint_section_view` for the initial section is received. (3) The app defers the first dataLayer push by one tick so GTM has a chance to initialize; ensure your GA4 tags are triggered by the custom event names (e.g. Custom Event equals `blueprint_section_view`), not only DOM Ready. |
| **SPA virtual page_view (History Change)** | The app pushes `virtual_page_view` and `blueprint_page_view` (with `page_path`, `page_title`, `page_location`) on each section change. If you use GTM, create a **GA4 Event** tag of type **page_view** triggered by **History Change** (e.g. trigger that fires on `gtm.historyChange` or `gtm.historyChange-v2`). Set the tag’s `page_location` to the full URL (or a variable from the pushed event), and `page_title` from the section/tab. Without this tag, GA4 will not receive a page_view per SPA route and Pages/session and engagement can be undercounted. |
| **Custom dimensions (GA4 Admin)** | In GA4 Admin → **Custom definitions** → **Custom dimensions**, register these event-scoped dimensions so they appear in reports and BigQuery: `section`, `tab`, `overview_section`, `element_type`, `element_id`, `step_number`. You can omit legacy UA-style `event_category`, `event_action`, and `event_label` if you adopt GA4-native parameter names only. |
| **Section values (taxonomy)** | All `section` dimension values use a single delimiter convention: **hyphen** for multi-word sections (e.g. `cross-domain`, `implementation-tools`, `account-profile`, `user-guide`, `server-side`). Single-word sections have no delimiter (e.g. `home`, `overview`, `workflow`, `framework`, `guides`, `checklist`, `interviews`, `resources`, `pricing`, `admin`). Use these values in GTM variables and GA4 filters for consistent regex and custom dimension mapping. |
| **Single dataLayer** | The app uses one `window.dataLayer` (GTM/GA4). No Adobe `adobeDataLayer` or other secondary analytics layer is implemented. If you add Adobe Analytics later, implement `adobeDataLayer` separately and keep a schema mapping so both layers stay consistent. |
| **Consent timestamp** | `consent_timestamp` in `blueprint_consent_update` is set with `new Date().toISOString()` at the time of the consent event (not from a stored or cached value). Use `consent_trigger` (`blueprint` | `cmp`) to segment initial resolution vs CMP user action. |
| **Single consent event** | Blueprint pushes only `blueprint_consent_update` for consent. Register only this event in GA4. If `cookie_consent_update` or another name appears (e.g. from a CMP), deprecate or map it in GTM so one event name is used for consent in G-XXXXXXXX. |
| **gtm.uniqueEventId gaps** | If your audit or capture tool shows gaps in `gtm.uniqueEventId` (e.g. 10 then 15), IDs in between may be internal GTM events (e.g. `gtm.dom`, `gtm.load`) or tag-fired pushes. Use GTM Preview to inspect all dataLayer pushes in sequence. |

---

## 3. Event catalog

**GA4-native parameters only.** The app no longer sends the legacy Universal Analytics triple (`event_category`, `event_action`, `event_label`). All events use purpose-built fields (e.g. `section`, `tab`, `contact_source`, `download_type`, `plan_slug`). In GTM, map GA4 event parameters from these native fields, not from the UA triple.

### 3.1 Navigation

#### `page_view` (initial / session anchor)
- **When:** Once per page load, as the very first push (in the same deferred tick as the first content event). Fired after consent is satisfied so GA4 has a session anchor before any other events.
- **Requires:** Tracking enabled (and consent when consent manager is used).
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_page_view` |
| page_location | string | Full URL (e.g. `https://app.example.com/dashboard`) |
| page_title | string | `document.title` |
| page_referrer | string | `document.referrer` |
| language | string | `navigator.language` (e.g. `en-US`) |
| session_id | string | Same as other events (e.g. `bp_1709876543210_abc12def`) |

**Note:** In GTM, create a trigger for this event and a GA4 Event tag of type `page_view` with `page_location`, `page_title`, `page_referrer`, and `language` so GA4 receives a proper session start.

---

#### `section_view`
- **When:** User navigates to a different section (sidebar, overview card, etc.)
- **Requires:** `track_sections` = true
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_section_view` |
| event_category | string | `navigation` |
| event_action | string | `view` |
| event_label | string | Section id (e.g. `cross-domain`, `pricing`) or `cross-domain:overview` (with tab) |
| section | string | Same as event_label (without tab suffix) |
| tab | string | Tab id for cross-domain (`overview`, `installation`, `configurations`) or implementation-tools (`ga-audit`, `gtm-integration`, `testing`, `snippets`); matches active tab at navigation time. |
| time_on_section_sec | number | *(When navigating away)* Seconds spent on the *previous* section. Use in Funnel Exploration to analyze drop-off (e.g. cross-domain bounces). |
| previous_section | string | *(When navigating away)* Section id the user just left. |
| navigation_source | string | `initial_load` (first section of session), `user_navigation` (click/route change), or `back_button` (popstate). Use to distinguish first-load from re-navigation. |

**Note:** `section` and `tab` are always sent as separate parameters. For `implementation-tools` and `cross-domain`, `section_view.tab` reflects the **active tab at navigation time** (same source of truth as `tab_switch`), so the two events never carry contradictory `tab` values. Each `section_view` (except the first) may include `time_on_section_sec` and `previous_section` for the prior section so cross-domain and other engagement can be measured.

---

#### `virtual_page_view`
- **When:** Same as `section_view` (each SPA route/section change). Pushed so GA4 can record page_view for engagement and funnel attribution.
- **Requires:** `track_sections` = true
- **Params:** `page_path`, `page_title`, `page_location`, `section`, `tab` (when applicable). When delivery is client or both, the app also pushes a second object with event `blueprint_page_view` and the same page_* params so GTM can fire a GA4 **page_view** event. In GTM, add a GA4 Event tag triggered by `blueprint_page_view` (or `gtm.historyChange-v2`) that sends event name `page_view` with `page_location`, `page_path`, `page_title` to avoid undercounted Pages/Session and Engagement Rate.

---

#### `blueprint_consent_update`
- **When:** Consent state is resolved on init (stored/default) or when the user interacts with the CMP (accept/decline). This is the **only** consent event name pushed by Blueprint; use it as the single source for consent signaling in GA4.
- **Params:** `consent_granted`, `consent_analytics`, `consent_advertising`, `consent_version`, `consent_method` (e.g. `stored`, `default`, `explicit_accept`, `explicit_decline`, `denied`), `consent_trigger` (`blueprint` = initial resolution from config/storage, `cmp` = user action in CMP), `consent_timestamp` (ISO string at event time).
- **GA4:** Register **only** this event name in G-XXXXXXXX for consent. If your CMP or another script pushes a different event (e.g. `cookie_consent_update`), do **not** register it as a separate GA4 custom event—either ignore it in GTM or map it to the same GA4 event so consent reporting stays on one event row.

---

#### `get_started_step_click`
- **When:** User clicks a get-started step button (steps 2–6: Add credentials, Extract requirements, etc.). Enables GA4 Funnel Exploration on onboarding.
- **Requires:** `track_buttons` = true
- **Params:** `step_number` (1-based; register as GA4 custom dimension), `step_name` and `event_label` (clean human-readable label only, e.g. `'Add credentials'` — no numeric prefix), `section`, `element_id` (e.g. `get-started-step-btn-2`). No generic `button_click` is sent for these.

---

#### `form_start`
- **When:** User focuses a form field for the first time (input, textarea, select). Fired once per form per page. Use with `form_submit` to measure abandonment.
- **Requires:** `track_forms` = true
- **Params:** `form_id`, `form_name`, `section`.

---

#### `filter_apply`
- **When:** User changes the Snippet Library filter (`#snippets-filter`), project tag filter (`#project-tag-filter`), or clicks an overview “All” filter control.
- **Params:** `filter_type` (`snippet_tag`, `project_tag`, or `content_category`), `filter_value` (e.g. `all`, `adobe`), `section`, `element_id` when applicable.

---

#### `tab_switch`
- **When:** User switches tabs within the cross-domain section
- **Requires:** `track_sections` = true
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_tab_switch` |
| event_category | string | `navigation` |
| event_action | string | `switch` |
| event_label | string | Human-readable (e.g. `cross-domain - installation`) |
| section | string | `cross-domain` |
| tab | string | Tab id (`overview`, `installation`, `configurations`) |

---

#### `code_copy`
- **When:** User copies code snippets, guide steps, interview questions, or code blocks. Fired inside the clipboard success callback, so it represents a successful copy (use as snippet_copied / copy confirmation in funnels).
- **Requires:** (always tracked)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_code_copy` |
| event_category | string | `engagement` |
| event_action | string | `copy` |
| section | string | Current section id |
| copy_type | string | `snippet`, `guide_step_code`, `interview_question`, `code_block`, or `code` |
| snippet_id | string | Snippet id (when copy_type is `snippet`) |
| code_block_id | string | Code block id (when copy_type is `code_block`) |

---

#### `credential_save_success`
- **When:** User saves a project credential from the Workflow panel (Measurement ID, API Secret, GA4 Property ID, or GTM Container ID) via the field’s change handler.
- **Requires:** Tracking enabled
- **Params:** `section` (`workflow`), `credential_type` (`measurement_id`, `api_secret`, `ga4_property_id`, or `container_id`), `event_category` (`engagement`). Use with get-started and section_view to measure discovery → credential setup.

---

#### `overview_card_click`
- **When:** User clicks an overview card on the Blueprint Overview page
- **Requires:** `track_sections` = true
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_overview_card_click` |
| event_category | string | `navigation` |
| event_action | string | `click` |
| event_label | string | Card title (e.g. `Cross Domain`, `GTM Integration`) |
| section | string | `overview` |
| overview_section | string | Section id the card navigates to |

---

#### `traffic_source`
- **When:** User visits with UTM parameters in the URL (once per session)
- **Requires:** Tracking enabled, at least one UTM param present
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_traffic_source` |
| event_category | string | `traffic` |
| event_action | string | `session_start` |
| event_label | string | Campaign or source value |
| utm_source | string | `indegene` |
| utm_medium | string | `email` |
| utm_campaign | string | `demo_session` |
| utm_term | string | (optional) |
| utm_content | string | (optional) |

---

#### `welcome_banner_view`
- **When:** A URL-triggered welcome banner is displayed
- **Requires:** Tracking enabled
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_welcome_banner_view` |
| event_category | string | `banner` |
| event_action | string | `view` |
| event_label | string | Banner title |
| welcome_banner_trigger_param | string | `utm_source` |
| welcome_banner_trigger_value | string | `indegene` |
| welcome_banner_title | string | Banner title |

---

#### `welcome_banner_dismiss`
- **When:** User dismisses a welcome banner (clicks close)
- **Requires:** Tracking enabled
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_welcome_banner_dismiss` |
| event_category | string | `banner` |
| event_action | string | `dismiss` |
| event_label | string | Banner title |
| welcome_banner_trigger_param | string | `utm_source` |
| welcome_banner_trigger_value | string | `indegene` |
| welcome_banner_title | string | Banner title |

---

### 3.2 Engagement

#### `button_click`
- **When:** User clicks a `button` or `[role="button"]` element
- **Requires:** `track_buttons` = true
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_button_click` |
| event_category | string | `engagement` |
| event_action | string | `click` |
| event_label | string | Button text, aria-label, title, or id (truncated to 200 chars) |
| section | string | Current section id |
| element_id | string | Button id if present |
| element_type | string | `button` |

---

#### `outbound_click`
- **When:** User clicks a link to an external domain (not same origin)
- **Requires:** `track_links` = true
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_outbound_click` |
| event_category | string | `outbound` |
| event_action | string | `click` |
| event_label | string | Link text or URL (truncated to 200 chars) |
| section | string | Current section id |
| element_id | string | Link id if present |
| element_type | string | `link` |
| link_url | string | Full href |
| outbound | boolean | `true` |

---

#### `form_submit`
- **When:** User submits a form (`<form>`)
- **Requires:** `track_forms` = true
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_form_submit` |
| event_category | string | `form` |
| event_action | string | `submit` |
| event_label | string | Form name or id |
| section | string | Current section id |
| form_id | string | Form id if present |
| form_name | string | Form `name` attribute or id |

---

### 3.3 Promo & contact

#### `promo_cta_click`
- **When:** User clicks a promo sidebar CTA (e.g. "View plans")
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_promo_cta_click` |
| event_category | string | `promo` |
| event_action | string | `click` |
| event_label | string | `View plans` |
| section | string | Section where promo was visible |
| promo_action | string | `pricing` |
| promo_type | string | `upgrade` |

---

#### `pricing_cta_click`
- **When:** User clicks a pricing card CTA (Get started or Subscribe)
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_pricing_cta_click` |
| event_category | string | `pricing` |
| event_action | string | `click` |
| event_label | string | Plan name (e.g. `Pro`, `Starter`) |
| section | string | `pricing` |
| plan_slug | string | `free`, `starter`, `pro`, `team` |
| pricing_cta_type | string | `get_started` (Free) or `subscribe` (paid) |

---

#### `contact_click`
- **When:** User clicks Contact (nav or footer)
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_contact_click` |
| event_category | string | `contact` |
| event_action | string | `click` |
| event_label | string | `Contact` |
| section | string | Current section id |
| element_id | string | `nav-contact-btn` or `footer-contact-btn` |
| contact_source | string | `nav` or `footer` |

---

### 3.4 GTM integration

#### `gtm_import`
- **When:** User successfully imports a GTM container JSON
- **Requires:** Tracking enabled
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_gtm_import` |
| event_category | string | `gtm_integration` |
| event_action | string | `import` |
| event_label | string | Container name |
| section | string | `gtm-integration` |
| gtm_action | string | `import` |
| gtm_tag_count | number | Number of tags in imported container |

---

#### `gtm_export`
- **When:** User exports a GTM-ready JSON from their project
- **Requires:** Tracking enabled
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_gtm_export` |
| event_category | string | `gtm_integration` |
| event_action | string | `export` |
| event_label | string | Filename (e.g. `MyProject-gtm-export.json`) |
| section | string | `gtm-integration` |
| gtm_action | string | `export` |
| file_name | string | Same as event_label |

---

#### `gtm_clear`
- **When:** User clears the imported GTM data
- **Requires:** Tracking enabled
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_gtm_clear` |
| event_category | string | `gtm_integration` |
| event_action | string | `clear` |
| event_label | string | `GTM import cleared` |
| section | string | `gtm-integration` |
| gtm_action | string | `clear` |

---

### 3.5 AI Assist

#### `ai_extract`
- **When:** User successfully runs AI requirements extraction (Project panel → AI: Extract requirements)
- **Requires:** Tracking enabled
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_ai_extract` |
| event_category | string | `ai` |
| event_action | string | `extract` |
| event_label | string | `success` |
| section | string | `project` |
| ai_action | string | `extract_requirements` |

---

#### `ai_extract_apply`
- **When:** User applies extraction results to project (Merge or Replace framework)
- **Requires:** Tracking enabled
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_ai_extract_apply` |
| event_category | string | `ai` |
| event_action | string | `apply` |
| event_label | string | `merge` or `replace` |
| section | string | `project` |
| ai_action | string | `apply_to_project` |
| apply_mode | string | `merge` or `replace` |

---

#### `ai_generate_guide`
- **When:** User successfully generates an implementation guide from gaps (Implementation Guides → Generate from gaps)
- **Requires:** Tracking enabled
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_ai_generate_guide` |
| event_category | string | `ai` |
| event_action | string | `generate` |
| event_label | string | `success` |
| section | string | `guides` |
| ai_action | string | `generate_from_gaps` |

---

#### `ai_import_url`
- **When:** User successfully imports a document from URL for AI extraction (Import from URL)
- **Requires:** Tracking enabled
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_ai_import_url` |
| event_category | string | `ai` |
| event_action | string | `import` |
| event_label | string | URL (truncated to 200 chars) |
| section | string | `project` |
| ai_action | string | `import_from_url` |
| url_length | number | Length of imported URL |

---

### 3.6 Mobile Features

#### `project_type_change`
- **When:** User changes project type (Web, Mobile, or Both) in project settings
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_project_type_change` |
| event_category | string | `project` |
| event_action | string | `change_type` |
| event_label | string | `web`, `mobile`, or `both` |
| section | string | Current section id |
| project_type | string | Same as event_label |
| project_id | string | Current project id |

---

#### `mobile_field_update`
- **When:** User updates a mobile-specific field (Firebase Project ID, iOS Bundle ID, Android Package, GTM Mobile Container ID)
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_mobile_field_update` |
| event_category | string | `project` |
| event_action | string | `update` |
| event_label | string | Field label (e.g. `Firebase Project ID`, `iOS Bundle ID`) |
| section | string | Current section id |
| field_name | string | Field key (e.g. `firebaseProjectId`, `iosBundleId`) |
| has_value | boolean | `true` if field has a value after update |
| project_id | string | Current project id |

---

#### `mobile_config_upload`
- **When:** User uploads a mobile config file (google-services.json or GoogleService-Info.plist)
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_mobile_config_upload` |
| event_category | string | `project` |
| event_action | string | `upload_config` |
| event_label | string | Filename (e.g. `google-services.json`) |
| section | string | Current section id |
| file_type | string | File extension (`json` or `plist`) |
| file_name | string | Same as event_label |
| project_id | string | Current project id or `null` |

---

#### `mobile_config_parsed`
- **When:** Mobile config file is successfully parsed and data extracted
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_mobile_config_parsed` |
| event_category | string | `project` |
| event_action | string | `parse_config` |
| event_label | string | `success` |
| section | string | Current section id |
| file_type | string | File extension (`json` or `plist`) |
| project_id | string | Current project id or `null` |

---

#### `mobile_config_parse_error`
- **When:** Mobile config file parsing fails
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_mobile_config_parse_error` |
| event_category | string | `project` |
| event_action | string | `parse_config` |
| event_label | string | `error` |
| section | string | Current section id |
| file_type | string | File extension (`json` or `plist`) |
| error_message | string | Error message from parser |
| project_id | string | Current project id or `null` |

---

#### `mobile_firebase_code_generated`
- **When:** User generates Firebase Analytics event code (iOS/Android) in Implementation Tools → Mobile tab
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_mobile_firebase_code_generated` |
| event_category | string | `mobile` |
| event_action | string | `generate_code` |
| event_label | string | `firebase_event` |
| section | string | Current section id |
| event_name | string | Firebase event name |
| has_params | boolean | `true` if event has parameters |
| param_count | number | Number of event parameters |
| project_id | string | Current project id or `null` |

**Note:** Code copy events for mobile Firebase code are tracked via `code_copy` with `copy_type: 'code_block'` and `code_block_id: 'mobile-firebase-event-code'`.

---

#### `mobile_gtm_code_generated`
- **When:** User generates GTM Mobile container code (iOS/Android) in Implementation Tools → Mobile tab
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_mobile_gtm_code_generated` |
| event_category | string | `mobile` |
| event_action | string | `generate_code` |
| event_label | string | `gtm_mobile` |
| section | string | Current section id |
| container_id | string | GTM Mobile container ID |
| project_id | string | Current project id or `null` |

**Note:** Code copy events for mobile GTM code are tracked via `code_copy` with `copy_type: 'code_block'` and `code_block_id: 'mobile-gtm-code'`.

---

#### `mobile_cross_platform_code_generated`
- **When:** User generates cross-platform test code (iOS/Android) in Implementation Tools → Mobile tab
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_mobile_cross_platform_code_generated` |
| event_category | string | `mobile` |
| event_action | string | `generate_code` |
| event_label | string | `cross_platform` |
| section | string | Current section id |
| scenario | string | Test scenario (e.g. `app_launch`, `screen_view`, `button_click`) |
| test_ios | boolean | `true` if iOS code is generated |
| test_android | boolean | `true` if Android code is generated |
| platforms | string | Comma-separated platforms (`ios`, `android`, or `ios,android`) |
| project_id | string | Current project id or `null` |

**Note:** Code copy events for cross-platform code are tracked via `code_copy` with `copy_type: 'code_block'` and `code_block_id: 'mobile-cross-platform-code'`.

---

### 3.7 GA4 Audit

#### `ga4_audit_run`
- **When:** User runs a GA4 Configuration Audit
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_ga4_audit_run` |
| event_category | string | `ga4_audit` |
| event_action | string | `run` |
| event_label | string | `success` or `error` |
| section | string | `ga-audit` |
| measurement_id | string | GA4 Measurement ID used for audit |
| has_domains | boolean | `true` if project has domains configured |
| save_to_history | boolean | `true` if "Save to history" checkbox was checked |

---

#### `ga4_audit_history_view`
- **When:** User views a previous audit from history
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_ga4_audit_history_view` |
| event_category | string | `ga4_audit` |
| event_action | string | `view_history` |
| event_label | string | Audit record ID |
| section | string | `ga-audit` |
| audit_id | string | Audit record ID from history |
| audit_score | number | Overall health score from the audit |
| audit_date | string | ISO date string of when audit was run |

---

#### `ga4_audit_history_delete`
- **When:** User deletes an audit record from history
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_ga4_audit_history_delete` |
| event_category | string | `ga4_audit` |
| event_action | string | `delete_history` |
| event_label | string | Audit record ID |
| section | string | `ga-audit` |
| audit_id | string | Audit record ID being deleted |

---

#### `ga4_audit_export`
- **When:** User exports the audit report
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_ga4_audit_export` |
| event_category | string | `ga4_audit` |
| event_action | string | `export` |
| event_label | string | `audit_report` |
| section | string | `ga-audit` |
| audit_score | number | Overall health score |
| finding_count | number | Total number of findings |
| critical_count | number | Number of critical findings |
| warning_count | number | Number of warning findings |

---

**Note:** GA4 Audit features include:
- Automated audit of GA4 property configuration
- Audit history with responsive grid layout (single item full-width, multiple items in 2-column grid)
- Health score panel matching height of GA4 Configuration Audit panel
- Compact Property Information display with inline fields and comma-separated domains
- Consistent audit history loading on page load and tab switch

---

### 3.8 Project Quick Start

#### `project_quick_start_create`
- **When:** User creates a new project from the quick-start interface on the overview page (either from the initial input field or the "Create New Project" dropdown option)
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_project_quick_start_create` |
| event_category | string | `project` |
| event_action | string | `create` |
| event_label | string | `quick_start_first` or `quick_start_new_option` |
| section | string | `overview` |
| project_id | string | New project id |
| project_name | string | Project name entered by user |
| creation_method | string | `quick_start_input` (first project) or `quick_start_dropdown` (from dropdown) |

---

#### `project_quick_start_select`
- **When:** User selects an existing project from the quick-start dropdown on the overview page
- **Requires:** Tracking enabled (no category toggle)
- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_project_quick_start_select` |
| event_category | string | `project` |
| event_action | string | `select` |
| event_label | string | `quick_start_dropdown` |
| section | string | `overview` |
| project_id | string | Selected project id |
| project_name | string | Selected project name |
| selection_method | string | `quick_start_dropdown` |

---

### 3.9 Downloads

#### `file_download`
- **When:** User exports or downloads certain files
- **Requires:** `track_downloads` = true
- **Tracked actions:**

| download_type | event_action | file_name | Trigger |
|---------------|--------------|-----------|---------|
| `deliverable_html` | `export` | `{project}-deliverable.html` | Export deliverable (HTML) |
| `deliverable_pdf` | `print` | (empty) | Print/Save as PDF from deliverable modal |
| `cross_domain_script` | `export` | `CrossDomainLinker.js` | Download Full Script (Cross Domain section) |
| `server_side_script` | `export` | `ga4-server.js` | Download server-side snippet |
| `checklist` | `export` | `{project}-implementation-checklist.md` | Export checklist markdown |
| `framework_csv` | `export` | `measurement-framework.csv` | Export framework CSV |
| `guide` | `export` | `{guide-name}.md` | Export guide markdown |
| `interview_questions` | `export` | `stakeholder-interview-questions.md` | Export interview questions |
| `interview_brief` | `export` | `{project}-interview-brief.md` | Export interview brief |
| `stakeholders` | `export` | `{project}-stakeholders.md` | Export stakeholders markdown |
| `backup` | `export` | `blueprint-backup-{date}.json` | Export backup JSON |

- **Params:**

| Param | Type | Example |
|-------|------|---------|
| event | string | `blueprint_file_download` |
| event_category | string | `download` |
| event_action | string | `export` or `print` |
| event_label | string | download_type value |
| section | string | Current section id |
| download_type | string | See table above |
| file_name | string | Output filename when known |

---

## 4. Data schema

### Standard payload (all events)

Every push includes:

| Field | Type | Description |
|-------|------|-------------|
| event | string | Prefixed event name (e.g. `blueprint_section_view`) |
| event_category | string | Category for GA (e.g. `navigation`, `engagement`) |
| event_action | string | Action (e.g. `click`, `view`, `submit`) |
| event_label | string | Label (element text, section id, etc.) |

### Conditional params (by event type)

| Param | Used by | Description |
|-------|---------|-------------|
| session_id | All | Client-generated id per page load (e.g. `bp_1709876543210_abc12def`) for session segmentation |
| ga4_client_id | dataLayer (once per page) | GA4 client_id read via `gtag('get', mid, 'client_id', …)` and pushed as a separate dataLayer object so GTM can expose it as a variable; supports anonymous→auth stitching |
| section | Most | Current app section |
| element_id | Buttons, links, contact | DOM element id (omitted when empty) |
| element_type | Buttons, links | `button` or `link` |
| form_id | form_submit | Form id |
| form_name | form_submit | Form name |
| link_url | outbound_click | Full URL |
| outbound | outbound_click | `true` |
| download_type | file_download | Type of download |
| file_name | file_download, gtm_export | Filename |
| tab | section_view, tab_switch, virtual_page_view | Tab id for cross-domain or implementation-tools section |
| step_number | button_click (get-started steps) | 1-based step index (e.g. 2 for "Add credentials") |
| page_path | virtual_page_view | SPA path (e.g. `/interviews`, `/cross-domain/overview`) |
| page_title | virtual_page_view | document.title at time of view |
| page_location | virtual_page_view | Full URL (origin + page_path) |
| filter_type | filter_apply | `snippet_tag`, `project_tag`, or `content_category` |
| filter_value | filter_apply | e.g. `all`, `adobe` |
| step_name | get_started_step_click | Human-readable step label |
| copy_type | code_copy | `snippet`, `guide_step_code`, `interview_question`, `code_block`, or `code` |
| snippet_id | code_copy | Snippet id (when copy_type is `snippet`) |
| code_block_id | code_copy | Code block id (when copy_type is `code_block`) |
| promo_action | promo_cta_click | e.g. `pricing` |
| promo_type | promo_cta_click | e.g. `upgrade` |
| plan_slug | pricing_cta_click | `free`, `starter`, `pro`, `team` |
| pricing_cta_type | pricing_cta_click | `get_started` or `subscribe` |
| contact_source | contact_click | `nav` or `footer` |
| gtm_action | gtm_* | `import`, `export`, `clear` |
| gtm_tag_count | gtm_import | Tag count |
| overview_section | overview_card_click | Section card navigates to |
| utm_source | traffic_source | UTM source |
| utm_medium | traffic_source | UTM medium |
| utm_campaign | traffic_source | UTM campaign |
| utm_term | traffic_source | UTM term |
| utm_content | traffic_source | UTM content |
| welcome_banner_trigger_param | welcome_banner_* | Banner trigger param (e.g. `utm_source`) |
| welcome_banner_trigger_value | welcome_banner_* | Banner trigger value (e.g. `indegene`) |
| welcome_banner_title | welcome_banner_* | Banner title |
| ai_action | ai_* | `extract_requirements`, `apply_to_project`, `generate_from_gaps`, `import_from_url` |
| apply_mode | ai_extract_apply | `merge` or `replace` |
| url_length | ai_import_url | Length of URL |
| measurement_id | ga4_audit_run | GA4 Measurement ID used for audit |
| has_domains | ga4_audit_run | `true` if project has domains configured |
| save_to_history | ga4_audit_run | `true` if audit was saved to history |
| audit_id | ga4_audit_history_* | Audit record ID |
| audit_score | ga4_audit_history_view, ga4_audit_export | Overall health score |
| audit_date | ga4_audit_history_view | ISO date string of audit |
| finding_count | ga4_audit_export | Total number of findings |
| critical_count | ga4_audit_export | Number of critical findings |
| warning_count | ga4_audit_export | Number of warning findings |
| project_type | project_type_change | `web`, `mobile`, or `both` |
| project_id | project_type_change, mobile_* | Current project id |
| field_name | mobile_field_update | Field key (e.g. `firebaseProjectId`) |
| has_value | mobile_field_update | `true` if field has value |
| file_type | mobile_config_* | `json` or `plist` |
| file_name | mobile_config_upload | Uploaded filename |
| error_message | mobile_config_parse_error | Error message |
| event_name | mobile_firebase_code_generated | Firebase event name |
| has_params | mobile_firebase_code_generated | `true` if event has parameters |
| param_count | mobile_firebase_code_generated | Number of parameters |
| container_id | mobile_gtm_code_generated | GTM Mobile container ID |
| scenario | mobile_cross_platform_code_generated | Test scenario name |
| test_ios | mobile_cross_platform_code_generated | `true` if iOS code generated |
| test_android | mobile_cross_platform_code_generated | `true` if Android code generated |
| platforms | mobile_cross_platform_code_generated | Comma-separated platforms |

---

## 5. Measurement framework

### Objectives and KPIs

| Objective | KPI | Events | Priority |
|-----------|-----|--------|----------|
| **Awareness** | App discovery, traffic sources | session_start, page_view | P0 |
| **Engagement** | Feature use, navigation depth | section_view, overview_card_click, button_click | P0 |
| **Consideration** | Interest in upgrade, content depth | promo_cta_click, pricing_cta_click, file_download, outbound_click | P1 |
| **Conversion** | Sign-ups, subscriptions, support | form_submit, contact_click | P0 |
| **Retention** | Return usage, GTM integration | gtm_import, gtm_export, section_view, ai_extract, ai_generate_guide | P1 |
| **Mobile adoption** | Mobile project usage, mobile feature engagement | project_type_change, mobile_field_update, mobile_config_upload, mobile_*_code_generated | P1 |

### Event-to-objective mapping

| Event | Objective | Notes |
|-------|-----------|-------|
| section_view | Engagement | Content consumption |
| overview_card_click | Engagement | Entry point for sections |
| button_click | Engagement | General interactions |
| form_submit | Conversion | Auth, contact, admin |
| outbound_click | Consideration | External resources, Stripe |
| promo_cta_click | Consideration | Upgrade intent (sidebar) |
| pricing_cta_click | Consideration | Plan selection (Get started / Subscribe by plan) |
| contact_click | Conversion | Support request |
| gtm_import | Retention | Power-user feature |
| gtm_export | Retention | Power-user feature |
| gtm_clear | Retention | Workflow cleanup |
| ai_extract | Retention | AI requirements extraction |
| ai_extract_apply | Retention | Apply extraction to project |
| ai_generate_guide | Retention | AI guide generation |
| ai_import_url | Retention | Import doc for extraction |
| file_download | Consideration | Deliverable/script value |
| project_type_change | Mobile adoption | Project type selection (web/mobile/both) |
| mobile_field_update | Mobile adoption | Mobile field configuration |
| mobile_config_upload | Mobile adoption | Mobile config file upload |
| mobile_config_parsed | Mobile adoption | Successful config parsing |
| mobile_config_parse_error | Mobile adoption | Config parsing errors (track for UX improvement) |
| mobile_firebase_code_generated | Mobile adoption | Firebase code generation usage |
| mobile_gtm_code_generated | Mobile adoption | GTM Mobile code generation usage |
| mobile_cross_platform_code_generated | Mobile adoption | Cross-platform test code generation |
| project_quick_start_create | Conversion | Project creation from overview quick-start |
| project_quick_start_select | Engagement | Project selection from overview quick-start |
| ga4_audit_run | Retention | GA4 Configuration Audit usage |
| ga4_audit_history_view | Retention | Viewing previous audit results from history |
| ga4_audit_history_delete | Retention | Deleting audit history records |
| ga4_audit_export | Retention | Exporting audit reports |
| ga4_audit_run | Retention | GA4 Configuration Audit usage |
| ga4_audit_history_view | Retention | Viewing previous audit results from history |
| ga4_audit_history_delete | Retention | Deleting audit history records |
| ga4_audit_export | Retention | Exporting audit reports |

### Key metrics

- **Sections per session** – engagement depth
- **Promo CTA rate** – upgrade interest
- **Pricing CTA by plan** – which plans get the most clicks (plan_slug, pricing_cta_type)
- **Contact rate** – support demand
- **GTM integration usage** – power-user adoption
- **AI feature usage** – extraction, apply, generate, import
- **Download rate** – value delivery (deliverable, scripts)
- **Mobile project adoption** – projects with mobile type (project_type_change)
- **Mobile field completion** – mobile fields filled (mobile_field_update with has_value: true)
- **Mobile config upload success rate** – successful vs failed parsing (mobile_config_parsed vs mobile_config_parse_error)
- **Mobile code generation usage** – Firebase, GTM Mobile, cross-platform code generation
- **Quick-start project creation rate** – projects created via overview quick-start (project_quick_start_create)
- **Quick-start project selection rate** – existing projects opened via quick-start (project_quick_start_select)

---

## 6. Implementation guide

### 6.1 Prerequisites

**For dataLayer only:**
- GTM container (or gtag/Adobe Launch) on the same page
- `window.dataLayer` defined before app scripts run (Blueprint initializes it when tracking is enabled)

**For client-side gtag:**
- GA4 Measurement ID (e.g. `G-XXXXXXXXXX`)
- Set it in Admin → Tracking and choose "Client-side (gtag.js)"

**For server-side Measurement Protocol:**
- Deploy the `ga4-measurement` Edge Function:  
  `supabase functions deploy ga4-measurement --project-ref YOUR_PROJECT_REF --no-verify-jwt`
- Add `GA4_API_SECRET` to Supabase Edge Function secrets (from GA4 Admin → Data Streams → Measurement Protocol API secrets)
- Set GA4 Measurement ID in Admin → Tracking and choose "Server-side (Measurement Protocol)" or "Both"

### 6.2 Enable tracking in Blueprint

1. Sign in as **admin**
2. Open **Admin** → **Tracking**
3. Check **Enable dataLayer tracking**
4. Set **Event name prefix** (e.g. `blueprint_` or leave default)
5. Toggle categories (buttons, forms, links, sections, downloads) as needed
6. **GA4 Measurement ID** (optional): Your GA4 Measurement ID for Blueprint app analytics
7. **Delivery method**: dataLayer only | Client-side | Server-side | Both
8. Click **Save tracking settings**

### 6.3 SPA page_view (History Change)

So GA4 receives a page_view per route and doesn’t undercount Pages/session:

1. In GTM, create a **Trigger** that fires on **History Change** (e.g. Event name equals `gtm.historyChange` or `gtm.historyChange-v2`).
2. Create a **GA4 tag** of type **page_view** (or GA4 Event with event name `page_view`).
3. Set **page_location** (e.g. `{{Page URL}}` or from the dataLayer push that contains `page_location`).
4. Set **page_title** from section/tab if desired (e.g. from the event’s `page_title` or `section`).
5. Set the tag to fire on the History Change trigger.
6. Publish and confirm in GA4 DebugView that each section change produces one page_view.

### 6.4 GTM triggers

Create triggers for each event or event pattern:

| Trigger name | Event name | Condition |
|--------------|------------|-----------|
| BP – Page view (session anchor) | Equals `blueprint_page_view` | Use for GA4 page_view tag on initial load |
| BP – Section view | Equals `blueprint_section_view` | — |
| BP – Overview card click | Equals `blueprint_overview_card_click` | — |
| BP – Traffic source | Equals `blueprint_traffic_source` | — |
| BP – Welcome banner view | Equals `blueprint_welcome_banner_view` | — |
| BP – Welcome banner dismiss | Equals `blueprint_welcome_banner_dismiss` | — |
| BP – Button click | Equals `blueprint_button_click` | — |
| BP – Form submit | Equals `blueprint_form_submit` | — |
| BP – Outbound click | Equals `blueprint_outbound_click` | — |
| BP – Promo CTA | Equals `blueprint_promo_cta_click` | — |
| BP – Pricing CTA | Equals `blueprint_pricing_cta_click` | — |
| BP – Contact click | Equals `blueprint_contact_click` | — |
| BP – GTM import | Equals `blueprint_gtm_import` | — |
| BP – GTM export | Equals `blueprint_gtm_export` | — |
| BP – GTM clear | Equals `blueprint_gtm_clear` | — |
| BP – AI extract | Equals `blueprint_ai_extract` | — |
| BP – AI extract apply | Equals `blueprint_ai_extract_apply` | — |
| BP – AI generate guide | Equals `blueprint_ai_generate_guide` | — |
| BP – AI import URL | Equals `blueprint_ai_import_url` | — |
| BP – File download | Equals `blueprint_file_download` | — |
| BP – Project type change | Equals `blueprint_project_type_change` | — |
| BP – Mobile field update | Equals `blueprint_mobile_field_update` | — |
| BP – Mobile config upload | Equals `blueprint_mobile_config_upload` | — |
| BP – Mobile config parsed | Equals `blueprint_mobile_config_parsed` | — |
| BP – Mobile config parse error | Equals `blueprint_mobile_config_parse_error` | — |
| BP – Mobile Firebase code generated | Equals `blueprint_mobile_firebase_code_generated` | — |
| BP – Mobile GTM code generated | Equals `blueprint_mobile_gtm_code_generated` | — |
| BP – Mobile cross-platform code generated | Equals `blueprint_mobile_cross_platform_code_generated` | — |
| BP – GA4 audit run | Equals `blueprint_ga4_audit_run` | — |
| BP – GA4 audit history view | Equals `blueprint_ga4_audit_history_view` | — |
| BP – GA4 audit history delete | Equals `blueprint_ga4_audit_history_delete` | — |
| BP – GA4 audit export | Equals `blueprint_ga4_audit_export` | — |
| BP – All events | Event name contains `blueprint_` | (optional catch-all) |
| BP – History Change (SPA page_view) | Equals `gtm.historyChange` or `gtm.historyChange-v2` | Use for GA4 page_view tag only |

### 6.5 GA4 event tags

For each trigger, create a **GA4 Event** tag:

- **Configuration tag:** Your GA4 config
- **Event name:** `{{Event}}` or a mapped name (e.g. `section_view`)
- **Event parameters:** Map dataLayer variables:

| GA4 param | dataLayer variable |
|-----------|-------------------|
| section | `{{dlv.section}}` |
| tab | `{{dlv.tab}}` |
| download_type | `{{dlv.download_type}}` |
| plan_slug | `{{dlv.plan_slug}}` |

Create **Data Layer Variables** in GTM for each param you need (e.g. `dlv.section`, `dlv.tab`). The app does not push `event_category`, `event_action`, or `event_label`.

### 6.6 GA4 custom dimensions (Admin → Custom definitions)

Register these as **event-scoped** custom dimensions in GA4 so they appear in reports and BigQuery:

| Dimension name | Scope | Notes |
|----------------|-------|--------|
| section | Event | Section id (e.g. overview, cross-domain) |
| tab | Event | Tab within section (e.g. overview, installation) |
| overview_section | Event | Section targeted by overview card |
| element_type | Event | e.g. button, link |
| element_id | Event | Element id when present |
| step_number | Event | Get-started step (1-based) |

You can omit **event_category**, **event_action**, and **event_label** from custom dimension registration if you rely on GA4-native parameter names.

### 6.7 Conversion events (GA4)

Mark these as conversions in GA4 if desired:

- `blueprint_promo_cta_click` – upgrade intent (sidebar)
- `blueprint_pricing_cta_click` – plan selection (pricing page)
- `blueprint_contact_click` – support request
- `blueprint_form_submit` (filter by `form_name` for sign-up/contact)

### 6.8 Testing

1. Enable tracking in Admin → Tracking
2. Open browser DevTools → Console
3. Run: `window.dataLayer`
4. Interact with the app (navigate, click buttons, etc.)
5. Confirm new objects appear in `dataLayer`

Or use **GTM Preview** to see events fire in real time.

---

## 7. Troubleshooting: GA4 not receiving events

If the app is set to send events server-side but GA4 shows no (or few) events, or the **GA4 Audit → Framework vs GA4** panel reports “42 framework event(s) not received”, check the following.

### Server-side delivery checklist

1. **Admin → Tracking → GA4 Measurement ID**  
   Must be set (e.g. `G-XXXXXXXXXX`). If empty, no events are sent to GA4.

2. **Admin → Tracking → Delivery**  
   Must be **“Server-side (Measurement Protocol)”** or **“Both client & server”**. If it is **“dataLayer” only**, events are only pushed to `dataLayer` and are not sent to GA4 by Blueprint.

3. **GA4_API_SECRET** (Supabase Edge Function secret)  
   For server-side delivery, the `ga4-measurement` Edge Function needs **GA4_API_SECRET** in Supabase → Edge Functions → Secrets. Use the same property’s **Measurement Protocol API secret** (GA4 Admin → Data Streams → your stream → Measurement Protocol API secrets). If this is missing, the function returns 400 and the browser request fails (silently).

4. **ga4-measurement deployed**  
   Deploy with:  
   `supabase functions deploy ga4-measurement --project-ref YOUR_REF --no-verify-jwt`

5. **Tracking enabled**  
   Admin → Tracking → “Enable dataLayer tracking” must be on. If using the consent manager, ensure the user has consented (or adjust consent mode).

### Framework vs GA4 audit: “missing” events

The audit compares your **measurement framework** event names (e.g. `section_view`) to event names GA4 has received in the last 30 days. If you use an **event name prefix** (e.g. `blueprint_`), Blueprint sends events like `blueprint_section_view`. The audit now strips this prefix when comparing, so framework events match GA4 received names. Ensure the **event name prefix** in Admin → Tracking matches what you use; the client sends this prefix to the audit so the comparison is correct. After redeploying the `ga4-audit` Edge Function with the prefix fix, re-run the audit.

---

## Summary: event quick reference

| Event | Category toggle | Fires when |
|-------|-----------------|------------|
| section_view | track_sections | Navigate to section (includes `tab` for cross-domain) |
| tab_switch | track_sections | Switch tabs within cross-domain section |
| overview_card_click | track_sections | Click overview card |
| traffic_source | (always) | Visit with UTM params (once per session) |
| welcome_banner_view | (always) | Welcome banner displayed |
| welcome_banner_dismiss | (always) | Welcome banner dismissed |
| code_copy | (always) | Copy code snippet, guide step, interview question, or code block |
| button_click | track_buttons | Click button |
| outbound_click | track_links | Click outbound link |
| form_submit | track_forms | Submit form |
| promo_cta_click | (always) | Click promo CTA (sidebar) |
| pricing_cta_click | (always) | Click pricing CTA (Get started / Subscribe) |
| contact_click | (always) | Click Contact (nav/footer) |
| gtm_import | (always) | Import GTM container |
| gtm_export | (always) | Export GTM JSON |
| gtm_clear | (always) | Clear GTM import |
| ai_extract | (always) | AI extraction success |
| ai_extract_apply | (always) | Apply extraction to project |
| ai_generate_guide | (always) | Generate guide from gaps |
| ai_import_url | (always) | Import doc from URL for extraction |
| file_download | track_downloads | Export deliverable HTML, scripts (CrossDomainLinker.js, ga4-server.js), checklist markdown, framework CSV, guide markdown, interview questions, stakeholders markdown, backup JSON, or print PDF |
| project_type_change | (always) | Change project type (web/mobile/both) |
| mobile_field_update | (always) | Update mobile field (Firebase Project ID, iOS Bundle ID, Android Package, GTM Mobile Container ID) |
| mobile_config_upload | (always) | Upload mobile config file (google-services.json or GoogleService-Info.plist) |
| mobile_config_parsed | (always) | Successfully parse mobile config file |
| mobile_config_parse_error | (always) | Error parsing mobile config file |
| mobile_firebase_code_generated | (always) | Generate Firebase Analytics event code (iOS/Android) |
| mobile_gtm_code_generated | (always) | Generate GTM Mobile container code (iOS/Android) |
| mobile_cross_platform_code_generated | (always) | Generate cross-platform test code (iOS/Android) |
| project_quick_start_create | (always) | Create project from overview quick-start (input or dropdown) |
| project_quick_start_select | (always) | Select existing project from overview quick-start dropdown |
| ga4_audit_run | (always) | Run GA4 Configuration Audit |
| ga4_audit_history_view | (always) | View audit from history |
| ga4_audit_history_delete | (always) | Delete audit record from history |
| ga4_audit_export | (always) | Export audit report |