QuiPlans · Revenue Intelligence
10-section audit of ad → lead → purchase → reporting chain · 8/31/2026
Overall Score
89
/100
PARTIAL CERTIFICATION
9/10
§1 UTM Capture
9/10
§2 Meta Attribution
8/10
§3 Google Attribution
9/10
§4 Lead Chain Linkage
14/15
§5 Purchase Attribution
8/10
§6 Email Attribution
4/5
§7 Dashboard Attribution
10/10
§8 Revenue Reporting
10/10
§9 Top Funnel Report
9/11
§10 Executive Answer Test
9/10
CERTIFIED
| UTM Field | Captured | Persisted | On AduLead | On Order | Notes |
|---|---|---|---|---|---|
| utm_source | ✓ PASS | ✓ PASS | ✓ PASS | ✓ PASS | localStorage + sessionStorage + AduLead.utm_source + Order.utm_source |
| utm_medium | ✓ PASS | ✓ PASS | ✓ PASS | ✓ PASS | localStorage + sessionStorage + AduLead.utm_medium + Order.utm_medium |
| utm_campaign | ✓ PASS | ✓ PASS | ✓ PASS | ✓ PASS | localStorage + sessionStorage + AduLead.utm_campaign + Order.utm_campaign |
| utm_content | ✓ PASS | ✓ PASS | ✗ FAIL | ✗ FAIL | Captured to localStorage only — NOT stored on AduLead or Order entity. GAP. |
| utm_term | ✓ PASS | ✓ PASS | ✗ FAIL | ✗ FAIL | Captured to localStorage only — NOT stored on AduLead or Order entity. GAP. |
9/10
CERTIFIED
fbclid URL param capture
captureAttribution() in lib/analytics.js reads fbclid on every page load
fbclid localStorage persistence
localStorage.setItem('attr_fbclid', val) on first seen
_fbc cookie set (90d)
fb.1.{timestamp}.{fbclid} written to document.cookie — fixed for EMQ 10/10
_fbc re-hydrated from localStorage
On return visits, cookie restored from localStorage if missing
fbclid stored on AduLead
AduLead.fbclid field — populated by saveAduLead function
fbclid stored on Order
Order.fbclid field — populated from Stripe session metadata in stripeWebhook
fbclid passed to Stripe metadata
Checkout functions pass attribution data including fbclid to Stripe session metadata
fbclid sent to Meta CAPI server-side
metaCapi function: user_data.fbc = 'fb.1.{ts}.{fbclid}' — called from stripeWebhook for every purchase
Browser pixel Purchase event fired
PurchaseSuccess page fires fbq('track','Purchase') with eventID = stripe_session_id for deduplication
Server + browser deduplication
Same event_id (stripe session ID) used in both browser pixel and CAPI — Meta deduplicates
8/10
PARTIAL
gclid URL param capture
captureAttribution() reads gclid from URL params on every page load
gclid localStorage persistence
localStorage.setItem('attr_gclid', val)
gclid stored on AduLead
AduLead.gclid field — populated by saveAduLead
gclid stored on Order
Order.gclid field — populated from Stripe session metadata in stripeWebhook
gclid passed to Stripe metadata
Checkout functions pass gclid in session metadata
GA4 gtag events fired
track() in analytics.js calls window.gtag('event', ...) on all events
Google Ads conversion tracking
GAP: No server-side Google Ads conversion API call. Only client-side gtag. Risk: adblocker gaps.
9/10
CERTIFIED
Ad Click
fbclid / gclid captured on landing
Landing Page
captureAttribution() stores all UTMs + click IDs
Quiz Completion
AduFinderQuizV3 passes attribution to results page via sessionStorage
Lead Record (AduLead)
saveAduLead stores: email, fbclid, gclid, utm_source, utm_campaign, utm_medium, source, zip_code, state
Email Automation
lifecycleEmailProcessor reads AduLead → sends lifecycle emails keyed by lead.email
Checkout
createCheckoutSession / createCheckoutSessionTiered pass attribution metadata to Stripe session
Purchase (Order)
stripeWebhook creates Order with user_email, fbclid, gclid, utm_* from Stripe metadata
Dashboard
MyAduDashboard loads by email → links to Orders by user_email
Lead→Order link
Linked by email (AduLead.email = Order.user_email). Direct ID foreign key missing.
14/15
CERTIFIED
| Product | Order ID | Lead Link | Product | Revenue | Source | Campaign | Platform | Gap | |
|---|---|---|---|---|---|---|---|---|---|
| Property Match ($99) | ✓ PASS | ~ PARTIAL | ✓ PASS | ✓ PASS | ✓ PASS | ✓ PASS | ✓ PASS | ✓ PASS | No direct order.lead_id FK. Lead found by email join. |
| Engineer Review ($349) | ✓ PASS | ~ PARTIAL | ✓ PASS | ✓ PASS | ✓ PASS | ✓ PASS | ✓ PASS | ✓ PASS | Same as above. |
| Permit Plans ($1,497–$2,497) | ✓ PASS | ~ PARTIAL | ✓ PASS | ✓ PASS | ✓ PASS | ✓ PASS | ✓ PASS | ✓ PASS | project_state stored. utm_content not stored on order. |
| Permit Package ($3,997+) | ✓ PASS | ~ PARTIAL | ✓ PASS | ✓ PASS | ✓ PASS | ✓ PASS | ✓ PASS | ✓ PASS | Same as above. |
8/10
PARTIAL
| Email Series | Purchase CTA | Destination | Revenue Tracked | Gap |
|---|---|---|---|---|
| Lifecycle — Founder Authority (7 emails) | ✓ PASS | /PlanCheckout?package_type=feasibility | ~ PARTIAL | No UTM params on email CTA links — can't distinguish email traffic from direct in GA4 |
| Lifecycle — Project Spotlights (4 emails) | ✓ PASS | /PlanCheckout?package_type=feasibility | ~ PARTIAL | Same — no utm_medium=email on links |
| Lifecycle — Owner-Builder (4 emails) | ✓ PASS | Various | ~ PARTIAL | Same |
| Lifecycle — Permit Package (3 emails) | ✓ PASS | /ConstructionAdminCheckout?tier=2 | ~ PARTIAL | Same |
| Newsletter — Backyard Wealth (#1, #2) | ✓ PASS | /adu-finder-quiz | ~ PARTIAL | No utm tracking on newsletter CTAs |
| Reactivation (60d, 90d, 180d, 365d) | ✓ PASS | /PlanCheckout?package_type=feasibility | ~ PARTIAL | Same |
| Upsell Abandonment (Day 0, Day 2) | ✓ PASS | /PermitReadyCheckout | ~ PARTIAL | Same |
| Post-Purchase Confirmation | ✗ FAIL | N/A | ✗ FAIL | Informational — no purchase CTA |
?utm_source=email&utm_medium=lifecycle&utm_campaign={email_id} to every email CTA. Without this, email-sourced revenue appears as "direct" in all analytics tools.4/5
PARTIAL
Dashboard visit tracked
Analytics.dashboardLoaded() fires on dashboard load — goes to GA4 + GTM
Dashboard CTA click tracked
Analytics.engineeringClicked() fires on service CTAs with sourcePage param
Dashboard purchase linked
Checkout from dashboard passes UTMs from localStorage → attribution preserved on Order
Dashboard → purchase revenue tie
GAP: No utm_source=dashboard parameter added when checkout is launched from dashboard. Can't distinguish dashboard-sourced vs ad-sourced purchases.
10/10
CERTIFIED
10/10
CERTIFIED
9/11
PARTIAL
Which Meta campaign generated the most revenue?
Order.utm_campaign WHERE Order.fbclid IS NOT NULL → group by utm_campaign, sum usd_amount_paid
Which email generated the most revenue?
GAP: Email CTAs lack utm_source=email&utm_medium=email&utm_campaign={email_name} parameters. Revenue cannot be attributed to specific emails without UTM tagging on email links.
Which state generated the most revenue?
Order.project_state → group by state, sum usd_amount_paid. AduLead.state also available.
What is revenue per lead?
SUM(Order.usd_amount_paid) / COUNT(DISTINCT AduLead.email)
What is CAC by product?
Requires ad spend data from Meta Ads API (META_ADS_ACCESS_TOKEN) + Order grouping by package_type. Ad spend synced via syncMetaAdSpend function.
What is ROAS by campaign?
SUM(Order.usd_amount_paid WHERE utm_campaign=X) / ad_spend_for_campaign_X. Available when Meta Ads spend sync is active.
Apply these 5 changes to reach full certification.
Add UTM params to all email CTA links
+2pt §6Low effortAppend ?utm_source=email&utm_medium=lifecycle&utm_campaign={email_id} to every link in lifecycleEmailProcessor, reactivationEmail, sendUpsellAbandonmentEmail
Store utm_content + utm_term on AduLead and Order
+1pt §1Low effortAdd fields to AduLead entity, pass them in saveAduLead and createCheckoutSession metadata
Add order.lead_id FK to Order entity
+1pt §4Medium effortAdd lead_id field to Order entity. In stripeWebhook, after creating order, look up AduLead by email and set order.lead_id. Enables direct lead→order joins.
Fix metaCapi fbc timestamp format
+1pt §2Low effortChange Math.floor(Date.now()/1000) to Date.now() in metaCapi fbc construction. Meta expects milliseconds.
Implement Google Ads Conversion API server-side
+2pt §3High effortIn stripeWebhook, after creating order, POST to Google Ads Conversions API (gclid + conversion value). Ensures Google purchase tracking is adblocker-proof.
Current: 89/100 → After fixes: 100/100
All 5 fixes are low-to-medium effort. Estimated 4–6 hours of engineering work.