Your Asset LibraryOutdoor Vibes

Lead Generation & Automation

Attribution & Tracking Stack Guide

How every lead is tracked end-to-end so you always know which ad, which brand, which dollar.


Outdoor Vibes / AZ Palm Tree Lights

Date: 2026-05-04 Timeline: 2–3 weeks to full operation Scope: CallRail + GA4 + Meta Pixel + Google Ads tag + n8n automation + Google Sheets + Looker Studio


Stack Overview

Traffic Sources (Meta, Google, Organic, GBP, Yard Signs)
        |
        v
Websites: azpalmtreelights.com + trimlightphoenix.com
        |
   +----+----+
   |         |
  Calls    Forms
   |         |
CallRail  Fillout
   |         |
   +----+----+
        |
       n8n (your n8n instance)
        |
   +----+----+
   |         |
Jobber   Google Sheets (Attribution Tracker)
                  |
            Looker Studio Dashboard

Monthly cost estimate: CallRail Essentials ~$45–75/mo + n8n (existing) + Looker Studio (free) = ~$45–75/mo incremental.


Pre-Flight Checklist

Before starting any section, confirm access to:

Meta Pixel: The pixel install for both sites is covered separately in marketing/meta-pixel-install-guide.md. Complete that guide first, then return here. Do not duplicate pixel setup in this guide.


Section 1 — CallRail Setup

Purpose: Track which marketing channel is driving phone calls. Each channel gets its own tracking number. Dynamic Number Insertion (DNI) swaps the number on the website in real time based on traffic source.

Timeline: 1–2 hours once account is open.

1.1 — Create the Account

  1. Go to callrail.com and sign up for a new account.
  2. Select plan: Essentials (~$45–75/mo depending on call volume and numbers). Includes:
    • Up to 5 local numbers
    • Call recording
    • Dynamic Number Insertion
    • Basic call analytics
    • Keyword-level tracking (tied to Google Ads)
  3. Company name: AZ Palm Tree Lights (keep separate from Trimlight; do not mix).
  4. Time zone: US/Mountain (Arizona — no DST).
  5. Billing: Jack's card — confirm with Jack.

1.2 — Provision Tracking Numbers

Provision one number per acquisition channel. Go to Numbers > Add Numbers > Local Number.

Tracking Number Slot Channel UTM Source Notes
AZPTL-Meta Paid social (Meta/IG ads) meta Swap on site when visitor comes from fb.com/instagram.com
AZPTL-Google Paid search (Google Ads) google Swap on site when visitor has gclid param
AZPTL-GBP Google Business Profile gbp Link from GBP listing directly to this number
AZPTL-Organic Organic / Direct organic Default swap when no UTM present
AZPTL-YardSign Yard signs / offline yard-sign Printed on physical signs; used in specific neighborhoods

Steps for each number:

  1. Numbers > Add Numbers > Local Numbers.
  2. Choose 480 area code (Scottsdale/Phoenix local).
  3. Forward to: (480) 631-7754 (the Quo/OpenPhone line).
  4. Label the number using the slot name above.
  5. Confirm ring time and voicemail settings — set to match main line behavior.

Note: All numbers forward to the same Quo/OpenPhone line. CallRail sits in front — it records attribution data, then rings through. Jack answers on OpenPhone as normal.

1.3 — Dynamic Number Insertion (DNI) Setup

DNI is a JavaScript snippet that automatically swaps the displayed phone number on the website based on the visitor's traffic source. This is the core attribution mechanism for calls.

How it works:

Install the DNI Script

  1. In CallRail: go to Numbers > your company > Swap Settings.
  2. Click Enable Dynamic Number Insertion.
  3. Copy the DNI script snippet — looks like:
    <script src="//cdn.callrail.com/companies/[COMPANY_ID]/[HASH]/cr.js"
            async="true"></script>
    
  4. Install on azpalmtreelights.com:
    • WordPress: use Insert Headers and Footers plugin (same plugin from Meta Pixel guide) — paste into Header section.
    • Squarespace: Settings > Advanced > Code Injection > Header.
  5. Install on trimlightphoenix.com (if tracking Trimlight calls too — confirm with Jack whether to add a separate Trimlight company in CallRail or skip for now).

Configure Number Pools

In CallRail, set up number swap rules for each tracking number:

  1. Go to Numbers > [number name] > Swap Rules.
  2. Set the following rules:

AZPTL-Meta — Swap Rules:

AZPTL-Google — Swap Rules:

AZPTL-GBP — Swap Rules:

AZPTL-YardSign — Swap Rules:

AZPTL-Organic — Swap Rules:

Verify DNI is Working

  1. Open azpalmtreelights.com in Chrome.
  2. Open DevTools > Network > filter for callrail.
  3. The cr.js script should load.
  4. Add ?utm_source=meta to the URL — the phone number displayed on the page should change to the AZPTL-Meta number.
  5. Remove the param — should revert to AZPTL-Organic (default).

1.4 — Call Recording Configuration

  1. In CallRail: Settings > Call Recording.
  2. Enable recording for all tracking numbers.
  3. Whisper message: set a short whisper for Jack so he knows the source before answering — e.g., "Call from Meta ads" (optional but useful).
  4. Recording announcement: Arizona is a one-party consent state — a recording disclaimer at call start ("This call may be recorded") is good practice and required for TCPA compliance. Enable it.
  5. Voicemail: configure if call is not answered — transcription on if available in plan.

1.5 — CallRail → Email/Slack Notifications (Optional)

Set up instant call notifications so no lead goes cold:

  1. Notifications > Add Notification > Email.
  2. Send to: Jack's email + Andrew's email.
  3. Trigger: Missed call (priority — these are lost leads).
  4. Include: caller ID, tracking number, recording link.

Section 2 — GA4 Setup

Purpose: Track all web behavior — page views, form submissions, phone click events — and link to Google Ads for conversion tracking.

Timeline: 2–3 hours.

2.1 — Create the GA4 Property

  1. Go to analytics.google.com.
  2. Sign in with the Google account that owns the Google Ads account (important for linking).
  3. Admin > Create Property.
  4. Property name: AZ Palm Tree Lights
  5. Reporting time zone: United States — Arizona (MST, no DST)
  6. Currency: USD
  7. Click Next — fill in business details (Home services / Small business).
  8. Click Create.
  9. When prompted, choose Web as the data stream.
  10. Enter https://azpalmtreelights.com.
  11. Stream name: AZ Palm Tree Lights - Web
  12. Copy the Measurement ID (format: G-XXXXXXXXXX) — you will need it for GTM.

2.2 — Install via Google Tag Manager (Recommended)

Using GTM keeps all tracking tags in one place and avoids code changes for every new tag.

Create GTM Container (if not already exists)

  1. Go to tagmanager.google.com.
  2. Create Account > Account Name: AZ Palm Tree Lights > Container Name: azpalmtreelights.com > Target: Web.
  3. Copy the GTM snippet — two parts: one for <head>, one for <body>.

Install GTM on the Website

WordPress:

  1. Install the "GTM4WP" plugin (Google Tag Manager for WordPress).
  2. In plugin settings, paste the Container ID (format: GTM-XXXXXXX).
  3. Enable placement in <head> and <body>.

Squarespace:

  1. Settings > Advanced > Code Injection.
  2. Header: paste the <head> GTM snippet.
  3. Footer: paste the <body> GTM snippet.

Add GA4 Tag in GTM

  1. In GTM: Tags > New.
  2. Tag type: Google Analytics: GA4 Configuration.
  3. Measurement ID: paste G-XXXXXXXXXX.
  4. Trigger: All Pages.
  5. Tag name: GA4 - Configuration.
  6. Save.

2.3 — Configure GA4 Events

Event 1: page_view (Auto)

GA4 fires page_view automatically via the Configuration tag. No additional setup needed. Verify in GA4 Realtime after publishing GTM.

Event 2: generate_lead (Form Submission)

This fires when a visitor submits the quote request form.

In GTM — create a trigger:

  1. Triggers > New > Form Submission.
  2. Fire on: Some Forms.
  3. Condition: Form URL contains /quote OR Form classes contain [your form's class].
    • If using Fillout embedded forms: use a custom event trigger instead (see below).
  4. Name: Trigger - Form Submit - Quote.

For Fillout embedded forms: Fillout fires a postMessage when a form is submitted. In GTM:

  1. Create a Custom Event trigger.
  2. Event name: fillout_form_submitted (or check Fillout's GTM docs for exact event name).
  3. Create a tag: GA4 Event, event name: generate_lead.
  4. Parameters to include:
    • form_id — dynamic variable from Fillout's data layer push
    • lead_source — pull from UTM (see below)

Alternative — simpler approach: If Fillout redirects to a thank-you page on submit, use a Page View trigger on that URL:

  1. Trigger: Page URL contains /thank-you or equals /quote-submitted.
  2. Tag: GA4 Event, name: generate_lead.
  3. This is the most reliable method. Configure Fillout to redirect on submission.

Event 3: phone_call_click

Fires when a visitor clicks a tel: link (tap-to-call).

In GTM:

  1. Triggers > New > Click - Just Links.
  2. Fire on: Some Link Clicks.
  3. Condition: Click URL starts with tel:.
  4. Name: Trigger - Phone Click.

Create the tag:

  1. Tags > New > GA4 Event.
  2. Event name: phone_call_click.
  3. Parameters:
    • phone_number — set to Click URL variable.
  4. Trigger: Trigger - Phone Click.
  5. Name: GA4 - phone_call_click.

Capture UTM Parameters as User Properties

This lets you see source/medium/campaign in GA4 reports per user session.

  1. In GTM, create Variables for each UTM param:
    • Variable type: URL > Query > utm_source → name: UTM Source
    • Repeat for utm_medium, utm_campaign, utm_content
  2. In the GA4 Configuration tag, add User Properties:
    • source = {{UTM Source}}
    • medium = {{UTM Medium}}
    • campaign = {{UTM Campaign}}

2.4 — Configure Conversions in GA4

  1. In GA4: Admin > Events.
  2. Find generate_lead — toggle Mark as conversion: ON.
  3. Find phone_call_click — toggle Mark as conversion: ON.
  4. Wait 24–48 hours for these to appear in Conversions reporting.

Events only appear in the Events list after they fire at least once. Use GTM Preview mode to fire them in test, or wait for real traffic.

2.5 — Link GA4 to Google Ads

This is required for importing GA4 conversions into Google Ads for bidding.

  1. In GA4: Admin > Google Ads Links.
  2. Click Link.
  3. Select the Google Ads account for AZ Palm Tree Lights.
  4. Enable Personalized advertising (needed for remarketing).
  5. Enable Auto-tagging in Google Ads if not already on — this adds gclid params automatically.
  6. Click Submit.

After linking (allow 24–48 hours), in Google Ads:

  1. Tools > Conversions > New conversion action > Import > Google Analytics 4.
  2. Import generate_lead and phone_call_click.
  3. Set conversion values if desired (e.g., generate_lead = $150 estimated value).
  4. Set conversion window: 30 days for leads (longer sales cycle, especially seasonal).

2.6 — Publish GTM

  1. In GTM: click Submit in the top right.
  2. Publish version — add description: "Initial GA4 + conversion tracking".
  3. After publishing, go to Preview mode and test on the live site.
  4. Verify in GA4 Realtime report: visit the site, submit a form, click a phone number — all three events should appear.

Section 3 — UTM Discipline

Purpose: Consistent UTM parameters are the foundation of the entire attribution stack. Inconsistent UTMs break every downstream report.

3.1 — Standard Naming Convention

All UTMs are lowercase, hyphen-separated. No spaces, no caps, no special characters.

utm_source (where traffic comes from)

Value Use
meta All Meta/Facebook/Instagram ads
google Google Ads (paid search)
gbp Google Business Profile organic clicks
email Email campaigns (Mailchimp or manual)
organic Unpaid social / SEO (when manually tagging)
yard-sign Physical yard signs
partner Referral partners

utm_medium (channel type)

Value Use
paid-social Meta/IG ads
paid-search Google Ads (search or PMax)
cpc Alternative for Google Ads if auto-tagged
email Email campaigns
organic Organic search/social
direct Direct traffic (rarely applied manually)
offline Yard signs, truck wraps, events

utm_campaign (campaign identifier)

Format: [brand]_[type]_[monthyear]

Value Use
azptl_leadgen_may26 AZ Palm Tree Lights lead gen, May 2026
azptl_retarget_may26 Retargeting campaign
trim_leadgen_may26 Trimlight lead gen (if running)
cx-survey-may-2026 Customer experience survey campaign
azptl_brand_may26 Brand awareness / reach objective

Update the month suffix when campaigns roll to a new month. Do not reuse old campaign names for new periods.

utm_content (ad creative identifier)

Format matches the naming used in Meta Ads Manager creative naming system.

Value Description
c1r_day_night Concept 1 Remix — day/night reveal
c11_video_loop Concept 11 — video loop
c2_static_blue Concept 2 — static blue palm
[concept]_[format] Continue this pattern

utm_term (keyword — Google Ads only)

Google Ads auto-populates {keyword} via ValueTrack. Set in ad destination URL template:

{lpurl}?utm_source=google&utm_medium=paid-search&utm_campaign=azptl_leadgen_may26&utm_term={keyword}&utm_content={creative}

3.2 — UTM Builder Google Sheet

Create a shared Google Sheet named "Outdoor Vibes — UTM Builder" in the shared Drive.

Sheet 1: Builder

Column Header Notes
A Base URL e.g., https://azpalmtreelights.com/get-a-quote
B utm_source Dropdown: meta / google / gbp / email / organic / yard-sign / partner
C utm_medium Dropdown: paid-social / paid-search / email / organic / offline
D utm_campaign Free text
E utm_content Free text
F utm_term Free text (optional)
G Final URL Formula: =A2&"?utm_source="&B2&"&utm_medium="&C2&"&utm_campaign="&D2&IF(E2<>"","&utm_content="&E2,"")&IF(F2<>"","&utm_term="&F2,"")

Sheet 2: URL Library

Pre-built and approved UTM URLs for all active campaigns. Columns: Date Created, Campaign, URL, Notes, Used In.

Add data validation (dropdown lists) in Sheet 1 columns B and C to enforce the approved values. Lock Sheet 2 so only Andrew can add rows.

Share: your team

3.3 — UTM Application Rules


Section 4 — n8n Workflows

Instance: your n8n instance

Setup prerequisite — Jobber: Jobber does not have native outbound webhooks. Use n8n's polling (scheduled trigger checking Jobber API for changes) or use Jobber's Zapier integration as a bridge into n8n via webhook. The recommended approach:

Jobber API credentials: Get OAuth token from Jobber Developer portal. Store as n8n credential: Jobber OAuth.

Google Sheets credential: Connect n8n to Google Sheets via OAuth. Create a dedicated service account or use Andrew's Google account.


WF1 — Form Submission → Jobber Lead + Google Sheet

Trigger: Fillout webhook (fires on form submit) Actions: Create Jobber lead with source fields, log row to Attribution Google Sheet

Steps:

  1. In Fillout: Go to the quote form settings > Integrations > Webhooks.
  2. Add webhook URL: https://your-n8n-instance/webhook/azptl-form-submit (create this endpoint in n8n).
  3. Fillout will POST form data as JSON on each submission.

n8n Workflow:

[Webhook: POST /azptl-form-submit]
    |
[Set node: Map fields]
    - lead_date = {{$now.toISO()}}
    - name = {{$json.body.name}}
    - email = {{$json.body.email}}
    - phone = {{$json.body.phone}}
    - source = {{$json.body.utm_source || 'direct'}}
    - medium = {{$json.body.utm_medium || 'direct'}}
    - campaign = {{$json.body.utm_campaign || ''}}
    - content = {{$json.body.utm_content || ''}}
    - notes = {{$json.body.message || ''}}
    |
[HTTP Request: Jobber GraphQL API]
    - Method: POST
    - URL: https://api.getjobber.com/api/graphql
    - Header: Authorization: Bearer {{credentials.jobber_token}}
    - Body: GraphQL mutation to create a Client + Request (lead)
      - Custom fields: source, medium, campaign, content
    - Save returned jobber_id
    |
[Google Sheets: Append Row]
    - Sheet: Attribution Tracker
    - Tab: Leads
    - Columns: lead_date, source, medium, campaign, content, name, phone, email,
               jobber_id, lead_status (set to 'new'), quote_amount (''),
               won_amount (''), won_date (''), notes

Jobber GraphQL — Create Request mutation (simplified):

mutation CreateRequest($input: RequestCreateInput!) {
  requestCreate(input: $input) {
    request {
      id
      client {
        id
        name
      }
    }
    userErrors {
      message
      path
    }
  }
}

Input includes client name/email/phone and request title (e.g., "Website Lead — Meta Ads"). Map custom fields for source attribution if Jobber custom fields are configured (see Section 7).


WF2 — CallRail Call Completed → Jobber Lead + Google Sheet

Trigger: CallRail webhook (fires when a call ends) Actions: Create/update Jobber lead, log to Attribution Google Sheet

Steps:

  1. In CallRail: Settings > Notifications > Webhooks.
  2. Add webhook: https://your-n8n-instance/webhook/azptl-callrail.
  3. Trigger: Post-call (fires after call ends, includes duration, recording URL, tracking number matched).

n8n Workflow:

[Webhook: POST /azptl-callrail]
    |
[IF node: Call duration > 30 seconds]
    - Yes → continue (real calls only; skip hang-ups and spam)
    - No → stop
    |
[Set node: Map source from tracking number]
    - IF tracking_number == AZPTL-Meta → source = 'meta', medium = 'paid-social'
    - IF tracking_number == AZPTL-Google → source = 'google', medium = 'paid-search'
    - IF tracking_number == AZPTL-GBP → source = 'gbp', medium = 'organic'
    - IF tracking_number == AZPTL-YardSign → source = 'yard-sign', medium = 'offline'
    - ELSE → source = 'organic', medium = 'direct'
    |
[HTTP Request: Search Jobber for existing client by phone]
    - GraphQL query: clients(filter: {phone: $phone})
    - If found: update existing client / create new request linked to them
    - If not found: create new client + request
    |
[HTTP Request: Jobber — Create Request]
    - Title: "Phone Lead — [source]"
    - Custom fields: source, medium, callrail_recording_url
    - Save returned jobber_id
    |
[Google Sheets: Append Row]
    - Same schema as WF1
    - lead_status = 'new'
    - notes = "Call: " + call_duration + "s | Recording: " + recording_url

WF3 — Jobber Job Won → Revenue Row in Attribution Sheet

Trigger: Scheduled poll every 15 minutes — check Jobber for jobs that changed to "won" / "active" status since last poll Actions: Look up original lead source from Attribution Sheet, write revenue row

Steps:

[Schedule Trigger: every 15 minutes]
    |
[HTTP Request: Jobber GraphQL]
    - Query jobs where status changed to ACTIVE (won) since last_checked timestamp
    - Pull: job id, client id, quote total, job title, created_at
    |
[IF node: any new won jobs?]
    - No → stop
    - Yes → continue
    |
[Google Sheets: Lookup]
    - Search Attribution Tracker for matching jobber_id
    - If found: get original source, medium, campaign, content
    |
[Google Sheets: Update Row]
    - Set lead_status = 'won'
    - Set won_amount = job total from Jobber
    - Set won_date = today
    |
[Google Sheets: Append Row — Revenue tab]
    - won_date, source, medium, campaign, jobber_id, won_amount
    - (This tab drives the revenue chart in Looker Studio)

State management: Store last_checked timestamp in n8n static data or a dedicated "Config" tab in the Google Sheet. Update it on every successful poll run.


WF4 — Weekly Attribution Digest Email

Trigger: Schedule — every Monday at 8:00 AM MST Actions: Query Google Sheet, calculate KPIs, send email to Andrew

Steps:

[Schedule Trigger: Monday 8:00 AM MST]
    |
[Google Sheets: Read All Rows — Leads tab]
    - Filter: lead_date in last 7 days
    |
[Code Node: Calculate KPIs]
    JavaScript:
    - Total leads by source
    - Won count and revenue by source
    - CPL by source (if ad spend data available — see note below)
    - Close rate by source (won / total leads)
    - Average deal size by source
    |
[Gmail / Send Email node]
    To: jack@outdoorvibes.com
    Subject: "Outdoor Vibes Attribution Digest — Week of [date]"
    Body (HTML table):

    | Source     | Leads | Won | Close Rate | Revenue | CPL*  |
    | meta       | 12    | 3   | 25%        | $4,200  | $28   |
    | google     | 8     | 2   | 25%        | $2,800  | $31   |
    | gbp        | 4     | 2   | 50%        | $2,600  | —     |
    | organic    | 3     | 1   | 33%        | $1,400  | —     |
    | yard-sign  | 1     | 0   | 0%         | $0      | —     |
    | TOTAL      | 28    | 8   | 29%        | $11,000 | —     |

    *CPL = ad spend / leads. Requires manual spend input (see below).

Ad spend input: Until a Google Ads / Meta API pull is set up, maintain a "Weekly Spend" tab in the Google Sheet. Andrew manually enters spend each Monday before the digest runs. The workflow reads from that tab.


WF5 — Monthly Baseline Comparison

Trigger: Schedule — 1st of every month at 9:00 AM MST Actions: Compare current 30-day period to locked May 2026 baseline, send comparison email

Steps:

[Schedule Trigger: 1st of month 9:00 AM MST]
    |
[Google Sheets: Read — Baseline tab]
    - Pull locked May 2026 baseline values (see Section 7)
    |
[Google Sheets: Read — Leads tab]
    - Filter: lead_date in last 30 days
    |
[Code Node: Calculate deltas]
    - Current leads vs baseline leads: +X% / -X%
    - Current close rate vs baseline: +X pts / -X pts
    - Current revenue vs baseline: +$X / -$X
    - Current avg deal size vs baseline
    |
[Gmail: Send Email]
    Subject: "Outdoor Vibes — Monthly Attribution vs Baseline — [month]"
    Body: side-by-side table — Baseline | Current | Delta

Critical: The baseline values are read-only. The workflow reads them but never writes to the Baseline tab. Lock the sheet tab (right-click tab > Protect range > your team).


Section 5 — Google Sheets Attribution Tracker

File name: Outdoor Vibes — Attribution Tracker Location: Shared Google Drive folder: Outdoor Vibes / Data Share: your team

Tab 1: Leads

This is the primary data table. n8n writes every lead here.

Column Header Type Notes
A lead_date Date ISO format, YYYY-MM-DD
B source Text From UTM or CallRail lookup
C medium Text From UTM or CallRail lookup
D campaign Text From UTM
E content Text From UTM
F name Text From form or CallRail caller ID
G phone Text E.164 format: +14806317754
H email Text From form (blank for calls)
I jobber_id Text Jobber request/client ID
J lead_type Text form / phone
K lead_status Text new / quoted / won / lost
L quote_amount Currency Updated when Jobber quote created
M won_amount Currency Updated by WF3 when job won
N won_date Date Updated by WF3
O notes Text Call duration, recording URL, etc.

Validation rules:

Tab 2: Revenue

Append-only revenue log. Written by WF3 when job is won.

Column Header
A won_date
B source
C medium
D campaign
E jobber_id
F won_amount

Tab 3: Baseline

Locked. Written once during Section 7 (Baseline Capture). Contains one row per metric.

Column Header
A metric
B value
C period
D notes

Rows: total_leads_90d, avg_monthly_leads, total_revenue_90d, avg_deal_size, close_rate, avg_monthly_calls, avg_monthly_forms

Tab 4: Weekly Spend

Manual entry by Andrew each Monday.

Column Header
A week_of
B meta_spend
C google_spend
D total_spend
E notes

Tab 5: UTM Library

Copy of the approved UTM URLs from Section 3.2. Read-only reference.


Section 6 — Looker Studio Dashboard

URL: looker.google.com (sign in with Google) Report name: Outdoor Vibes — Attribution Dashboard Data source: Google Sheets — Attribution Tracker (both Leads and Revenue tabs) Share read-only with Jack: once built, use "Share > Manage Access" to add Jack's email as Viewer.

Setup: Connect Data Source

  1. In Looker Studio: Create > Report.
  2. Add data: Google Sheets > select the Attribution Tracker file.
  3. Add the Leads tab as one data source.
  4. Add the Revenue tab as a second data source.
  5. For date fields, confirm Looker Studio recognizes lead_date and won_date as Date type.

Page 1 — Executive Summary

Purpose: Weekly/monthly health check for Andrew and Jack. Answers: "Is the attribution stack working and is marketing ROI positive?"

Layout (top to bottom):

Row 1 — Scorecards (6 total)

Row 2 — Bar chart: Leads by Source

Row 3 — Two charts side by side:

Row 4 — Date filter control


Page 2 — Channel Deep Dive

Purpose: Compare channel efficiency — which source produces the best ROI?

Table: Channel Performance Summary

Dimensions: source, medium Metrics:

Time Series Chart: Leads Over Time by Source

Scatter Chart: Close Rate vs Lead Volume by Source

Filter controls on this page:


Page 3 — Pipeline

Purpose: See what's in the funnel right now — are there stale leads that need follow-up?

Funnel Chart: Lead Status Steps: new → quoted → won / lost Values: count of leads in each status

Table: Open Leads (not won or lost)

Stale Lead Count Scorecard

Won Jobs — Revenue Timeline


Looker Studio Configuration Notes


Section 7 — Baseline Capture Protocol

Purpose: Lock a pre-attribution baseline so future results can be compared against a known starting point. This is the "before" snapshot. Do it once, never change it.

Timing: Complete this before any n8n workflows go live and before any tracked campaigns run. Do it now, in the first week.

Document to create: Outdoor Vibes — Attribution Baseline — May 2026 (Google Doc in shared Drive)

7.1 — Data to Pull from Jobber

Log in to Jobber with Jack present (or have Jack export). Pull the following for the period February 1 – April 30, 2026 (last 90 days as of May 1):

  1. Total leads received — count of new requests/leads in Jobber
  2. Total quotes sent — count of quotes/estimates sent to clients
  3. Total jobs booked (won) — count of jobs that moved to active/completed
  4. Total revenue booked — sum of won job values
  5. Average deal size — total revenue / won jobs
  6. Quote-to-win conversion rate — won jobs / quotes sent
  7. Lead-to-win conversion rate — won jobs / total leads (if tracked)
  8. Monthly average for each above — divide 90-day totals by 3

Also capture:

7.2 — Baseline Document Structure

Create the Google Doc with this structure:

Outdoor Vibes — Attribution Baseline — May 2026
Locked: [date]. Do not modify.

Period: February 1 – April 30, 2026 (90 days)
Data source: Jobber export + manual count

== Lead Volume ==
Total leads (90d): [X]
Avg leads/month: [X]
Monthly breakdown: Feb [X] / Mar [X] / Apr [X]

== Conversion ==
Total quotes sent (90d): [X]
Total jobs won (90d): [X]
Quote-to-win rate: [X]%
Lead-to-win rate (if available): [X]%

== Revenue ==
Total revenue booked (90d): $[X]
Avg monthly revenue: $[X]
Avg deal size: $[X]

== Channel Attribution (Pre-Stack) ==
Primary inbound channels (estimated, not tracked): word of mouth, referral, GBP
Google Ads: not running (as of baseline date)
Meta Ads: limited / just starting
Tracking: none — all attribution manual/estimated

== Phone ==
Avg monthly calls: [X] (from Quo log if available)
Business number: (480) 631-7754

== Notes ==
[Any context — seasonal adjustments, notable events, one-time big jobs to exclude]

7.3 — Lock the Baseline

  1. In the Google Doc: File > Document Details — note the creation date.
  2. Move to a locked folder in Drive: Outdoor Vibes / Baseline — LOCKED.
  3. In the Attribution Google Sheet, populate the Baseline tab (Tab 3) with the key metrics.
  4. Right-click the Baseline tab > Protect Sheet > Your team can edit.
  5. Add a note in the doc header: "This document is final. It represents pre-attribution performance. Do not edit."

Section 8 — Testing Checklist

Run this end-to-end test for each channel before declaring the attribution stack live. For each test, start from scratch (incognito window) to simulate a real visitor.

Test 0 — Baseline Verification (Before Going Live)


Test 1 — Meta Ads Channel

Simulate: Visitor clicks Meta ad → lands on site → submits form.

  1. Open incognito window.
  2. Navigate to: https://azpalmtreelights.com/get-a-quote?utm_source=meta&utm_medium=paid-social&utm_campaign=azptl_leadgen_may26&utm_content=c1r_day_night
  3. Check: Phone number displayed on page = AZPTL-Meta tracking number (not the main 480 number).
  4. Check: GA4 Realtime shows session with source = meta.
  5. Submit the quote form with test data (use a real phone number you can verify).
  6. Check: GA4 Realtime shows generate_lead event.
  7. Check: n8n WF1 execution log shows success.
  8. Check: Jobber has a new client/request titled "Website Lead — Meta Ads" with source custom field = "meta".
  9. Check: Google Sheet (Leads tab) has a new row with source = "meta".
  10. Click the displayed phone number (on mobile or simulate a click on desktop).
  11. Check: GA4 Realtime shows phone_call_click event.

Test 2 — Google Ads Channel

  1. Open incognito window.
  2. Navigate to: https://azpalmtreelights.com?gclid=test123&utm_source=google&utm_medium=paid-search&utm_campaign=azptl_leadgen_may26
  3. Check: Phone number = AZPTL-Google tracking number.
  4. Submit form.
  5. Check: All same as Test 1 but source = google.

Test 3 — Phone Call (CallRail)

Simulate: Real inbound call to a tracking number.

  1. Call the AZPTL-Meta tracking number from a mobile phone.
  2. Let it ring through to the Quo/OpenPhone line.
  3. Answer and have a 30+ second conversation (or use a test script — "Hi, this is a test call").
  4. Hang up.
  5. Wait 2–5 minutes for CallRail webhook to fire.
  6. Check: n8n WF2 execution log shows success.
  7. Check: Jobber has a new client/request titled "Phone Lead — meta".
  8. Check: Google Sheet row written with source = meta, lead_type = phone, notes = recording URL.

Test 4 — GBP Channel

  1. Open incognito window.
  2. Navigate to: https://azpalmtreelights.com?utm_source=gbp&utm_medium=organic
  3. Check: Phone number = AZPTL-GBP tracking number.
  4. Submit form.
  5. Check: Attribution Sheet source = gbp.

Test 5 — Organic / Direct Channel

  1. Open incognito window.
  2. Navigate to https://azpalmtreelights.com with no UTM params.
  3. Check: Phone number = AZPTL-Organic (default number).
  4. Submit form.
  5. Check: Attribution Sheet source = organic (or direct).

Test 6 — End-to-End Looker Studio

After running Tests 1–5:

  1. Open the Attribution Google Sheet — Leads tab should have 5+ rows from tests.
  2. Open Looker Studio report.
  3. Check: All 5 test leads appear in the data.
  4. Check: Bar chart shows leads by source (meta, google, gbp, organic).
  5. Check: Page 3 Pipeline shows test leads in "new" status.
  6. Simulate a lead moving to "won": manually change one row in Sheet to lead_status = won, add won_amount.
  7. Check: Looker Studio reflects the change within 1 hour (cache).

Test 7 — Weekly Digest Email

  1. In n8n WF4, set the schedule to 2 minutes from now (temporary).
  2. Make sure there is test data in the Sheet.
  3. Run the workflow.
  4. Check: Email arrives with correct table of leads/revenue by source.
  5. Reset the schedule to Monday 8:00 AM MST.

Go-Live Sequence

Execute in this order to avoid data gaps:

Week 1 (Days 1–3):

  1. Complete Baseline Capture (Section 7) — lock it before anything else
  2. Create CallRail account + provision all 5 tracking numbers
  3. Install DNI script on azpalmtreelights.com
  4. Configure DNI swap rules for all channels
  5. Verify DNI with browser test (Section 8, Test 0)

Week 1 (Days 4–5): 6. Create GA4 property 7. Create/configure GTM container 8. Install GTM on site, add GA4 Configuration tag 9. Configure form + phone_call_click events in GTM 10. Publish GTM, verify in Realtime 11. Link GA4 to Google Ads

Week 2 (Days 1–3): 12. Create Attribution Google Sheet with all tabs 13. Build n8n WF1 (form → Jobber + Sheet) 14. Build n8n WF2 (CallRail → Jobber + Sheet) 15. Run Tests 1–5 (channel tests) 16. Fix any mapping issues

Week 2 (Days 4–5): 17. Build n8n WF3 (Jobber won → revenue row) 18. Build n8n WF4 (weekly digest) 19. Build n8n WF5 (monthly comparison) 20. Run Test 7 (digest email test)

Week 3: 21. Build Looker Studio dashboard (Pages 1–3) 22. Run Test 6 (Looker Studio end-to-end) 23. Share with Jack 24. Brief Jack: "Here's what you can see, here's what it means" 25. Apply UTM params to all active Meta ads 26. Update GBP listing URL with UTM params 27. Attribution stack is live — monitor daily for first 2 weeks


Quick Reference

System URL / Access
CallRail app.callrail.com
GA4 analytics.google.com
Google Tag Manager tagmanager.google.com
Google Ads ads.google.com
n8n your n8n instance
Jobber getjobber.com
Jobber GraphQL API api.getjobber.com/api/graphql
Jobber Developer Portal developer.getjobber.com
Looker Studio lookerstudio.google.com
Meta Pixel Install Guide marketing/meta-pixel-install-guide.md
Attribution Google Sheet [add URL once created]
Looker Studio Report [add URL once built]
Main Phone (Quo/OpenPhone) (480) 631-7754
AZ Palm Tree Lights site azpalmtreelights.com
Trimlight Phoenix site trimlightphoenix.com

Part of your Outdoor Vibes asset library — yours to keep and run.

— Andrew