Aaron Suarez · ← The Library Grimoire 001 · ASMC Full Stack CRM Engine
Grimoire 001 · Full-Stack CRM Engine · v8.1 · QA-Verified
● Live 24 Zaps 76 Fields v8.1 · QA-Verified
Map
Build
Demo
What this phase does and how it connects
Phase 00 · System Core

The Closed-Loop Revenue System

This is not a CRM setup or a funnel. It is a closed-loop, self-monitoring, self-correcting revenue system. Every tool, field, Zap, and sequence plugs into one loop: INPUT → NORMALIZE → RESPOND → TRACK → DECIDE → ROUTE → CONVERT → ANALYZE → CORRECT → LOOP. Reporting is not a layer on top — it is embedded in every action. The system does not wait to be told what to do. It detects stagnation and fixes it.
Self-healingIntent-scoredSLA-enforced24 Zaps76 Fields verifiedZero dead leads
v8.1 QAQC correction log — what was fixed from v8.0
Itemv8.0 Issuev8.1 Fix
Field countStated "95 fields" — overcountedCorrected throughout: 76 custom fields. Verified: 8+16+8+19+13+7+5 = 76 ✓
Zapier planStated "$20/mo Starter" — wrong plan for scheduled ZapsProfessional ($49/mo) minimum. Starter/Core plans cannot run Zaps 12, 15, 17, 18, 22, 23, 24.
Zap 8 filter bugsnags_open compared as text "0" — Zap never clearedUse: Number is less than 1 — not text match. Text "0" ≠ Number 0 in Zapier filters.
Zap 10 double-fireNo filter on first trigger — fires twice if field re-savedFilter added: fire only if deposit_invoice_date is currently empty.
Duplicate logic orderSearched email before phone — creates duplicate on re-submit with new emailSearch phone FIRST, then email. Same phone = same person, always.
Zap 4 tag triggerSpace before colon in tag name breaks GHL triggerExact tag format: Objection:Price — no space before colon. Test tag triggers with exact string.
Apple Mail open inflationNot flagged — scores email opens from Apple prefetch as real engagementFlag opens where user_agent contains "Apple". Score email clicks only — they are unambiguous.
Bland AI prerequisiteMentioned but not enforced as a hard prerequisitePathway config (script + disposition map) MUST be complete before Zap 5 activates. Never activate blind.
PandaDoc/Stripe double-chargeNot explicitly separated — both can trigger paymentPandaDoc = PDF document record. Stripe = payment flow. Never use PandaDoc's built-in payment if Stripe is primary processor.
Automation alternativesZapier-only — no alternatives documentedMake (Integromat), n8n, and ActivePieces full equivalents added. See Phase 24 — Automation Alternatives.
Permit tracking"File permit" task oversimplified — permit takes 2–8 weeksRealistic note added: application ≠ issuance. Never promise start date that depends on permit issuance.
Zap 13 no-show loopPM must clear site_visit_outcome or Zap fires repeatedlyExplicit requirement: PM updates site_visit_outcome after every site visit — blank field = Zap re-fires daily.
The 11-step closed loop
1
INPUT All entry points
Ads (Meta/Google) · Form submits · Inbound calls (CallRail) · Manual entry · SMS reply. On entry: Contact created/updated → lifecycle_stage = TOFUengagement_status = NEWintent_score = 0 → Opportunity created → Owner assigned → timestamp_created stored. Reporting starts here — not later.
2
NORMALIZE Anti-chaos engine
Immediately after entry: Does contact have lifecycle_stage? Owner? Opportunity? Source? If anything missing → auto-fix. Duplicate check: if phone/email exists → merge, keep highest intent_score, keep latest activity. This prevents data fragmentation — the silent killer of CRM systems.
3
RESPOND Speed + first impression
SMS fires instantly. Email fires at 2 min. Wait 5 min → check reply. first_response_timer starts at contact creation. When lead replies: first_response_time = NOW − createdengagement_status = ENGAGEDintent_score += 3. Response is not just action — it is measured behavior.
4
TRACK INTENT Core brain
Every behavioral event updates intent_score and last_activity_timestamp: SMS reply (+3) · Email click (+2) · Page visit (+1) · Call activity (+3) · Booking created (+5) · Estimate viewed (+4). Score also feeds reporting in real-time: engagement rate, activity density, time-to-action.
5
CALLS High-weight signals
Inbound call → intent_score += 3 · Missed call → auto-SMS + callback task · Connected call → outcome tagging within 2min · Outbound no-answer → retry sequence. All calls tracked: response time, contact rate, call-to-book rate.
6
PIPELINE MOVEMENT Data generator
Every stage move triggers: lifecycle_stage update → entered_stage_timestamp set → time_in_stage reset. Logs: conversion progression, drop-off points, stage velocity. Pipeline is not visual — it is a data engine.
7
DECIDE + ROUTE Operations brain
System evaluates: intent_score + source quality + stage + time delays → decides: assign owner / escalate / trigger AI call / move stage / recycle. SLA enforcement runs in parallel — 5min/15min/30min/1hr escalation ladder fires automatically.
8
CONVERT BOFU
Lead books → BOFU → confirmation + reminders. After appointment: Won → revenue logged + Phase 3 trigger → Lost → recycle → No-show → rebooking loop. Close rate, show rate, revenue attribution all logged automatically.
9
STAGNATION DETECTION Self-healing
Continuous monitoring: TOFU > 24hr → problem flag. MOFU > 3 days → nurture issue. BOFU > 2 days → sales issue. System actions: create task → escalate → reassign → force movement. No lead sits idle without a trip wire on it.
10
RECYCLE + RE-ENTRY Long-term money
When lead cools: move to RECYCLE → enter 12-month passive nurture. When any activity returns: move to MOFU → intent_score += boost → instant response triggered. Tracking: reactivation rate, time-to-re-engage, revenue from recycled leads.
11
REPORTING EMBEDDED Not a layer
Every action above feeds metrics in real time: INPUT (volume, source quality) → RESPONSE (speed) → ENGAGEMENT (intent flow) → PIPELINE (movement) → CONVERSION (revenue) → RECYCLE (recovery rate). You see where leads slow, where they drop, where they convert, where revenue comes from.
SLA escalation ladder — enforced automatically
0 min
SMS auto-sent
2 min
Email auto-sent
5 min
Reply check → if none → re-SMS
15 min
Rep task created
30 min
Reassign if rep inactive
60 min
Manager Slack alert
Intent score engine — every event has a weight
Behavioral signals (+)
SMS reply+3
Email click+2
Email open+1 ⚠ Flag opens where user_agent contains "Apple" — Apple Mail prefetches emails, inflating scores with fake opens. Score clicks only as reliable signal.
Page visit+1
Call connected+3
Booking created+5
Estimate viewed+4
Form scoring (lead quality)
Budget: Premium+10
Budget: Standard+5
Budget: Starter+2
Timeline: Urgent+15
Timeline: Near+10
Timeline: Long+5
Source: Referral+8 bonus
Routing thresholds
Score 21–25BOFU · Path 1 · AI call <5min
Score 16–20BOFU · Path 2 · Rep call 4hr
Score 11–15MOFU · Path 3 · Education
Score 6–10MOFU · Path 4 · Long cycle
Score 0–5TOFU · Path 5 · Standard
intent_score ≥5→ promote to BOFU
Inactivity 7d→ RECYCLE
New lifecycle fields — added to all contacts
FieldTypeValuesWritten by
lifecycle_stageDropdownTOFU / MOFU / BOFU / RECYCLE / CLIENT / PAST_CLIENTSystem (auto)
engagement_statusDropdownNEW / ENGAGED / RESPONDING / STALLED / COLD / DNCSystem (auto)
intent_scoreNumberRunning total — behavioral + form score. Resets on recycle.System (auto)
first_response_timeNumberMinutes from contact creation to first replySystem (auto)
last_activity_timestampDateTimeUpdated on every behavioral eventSystem (auto)
entered_stage_timestampDateTimeSet every time pipeline stage changesSystem (auto)
time_in_stageNumberHours in current stage — drives stagnation detectionCalculated
recycle_countNumberHow many times this contact has been recycledSystem (auto)
Build · System Loop
Implementing the loop in GHL + Zapier
The loop is not a single Zap — it's a set of GHL workflows and Zapier checks that work together. Build these foundation workflows before any phase-specific Zaps.
Foundation GHL workflows (build first)
WF-1
Contact created/updated → normalize + lifecycle assign
T
Trigger: Contact Created OR Contact Updated
1
Check: if lifecycle_stage is empty → set TOFU
If engagement_status is empty → set NEW. If intent_score is empty → set 0.
2
Check: if owner is empty → assign to default rep queue
3
Check: if no open opportunity → create opportunity in Lead In stage
4
If phone OR email already exists on another contact → flag for merge review
CriticalGHL does not auto-merge — create a task "Duplicate review: {{first_name}}" assigned to admin. Monthly dedup sweep in addition.
WF-2
Stagnation monitor — runs daily at 8am
T
Trigger: Daily scheduled workflow (8:00am business timezone)
F
Branch 1: TOFU contacts with last_activity_timestamp > 24hr ago
Action: Set engagement_status = STALLED. Create rep task "STALLED — {{first_name}} — 24hr no activity." Enrol in SMS re-engagement.
F
Branch 2: MOFU contacts with last_activity_timestamp > 3 days ago
Action: Create task "MOFU STALLED — {{first_name}} — 3 days." Move to Stalled pipeline stage. Send nurture email.
F
Branch 3: BOFU contacts with last_activity_timestamp > 48hr ago
Action: Create manager task "BOFU at risk — {{first_name}} — no activity 48hr." Flag in Slack #sales.
F
Branch 4: Any contact no activity 7+ days → move to RECYCLE
Action: lifecycle_stage = RECYCLE. Enrol in 12-Month Passive Nurture. Remove from active sales sequences.
WF-3
Intent score updater — on every behavioral event
T
Trigger: GHL email event (open/click) OR SMS reply OR call activity
2
Code step: calculate score delta
// weight map
const w = {sms_reply:3, email_click:2, email_open:1, page_visit:1, call_connected:3, booking:5, estimate_viewed:4};
const delta = w[inputData.event_type] || 0;
const newScore = parseInt(inputData.current_intent_score || 0) + delta;
const stage = newScore >= 5 ? 'BOFU' : newScore >= 3 ? 'MOFU' : 'TOFU';
output = [{newScore, stage, delta}];
3
Update contact: intent_score, lifecycle_stage (if promoted), last_activity_timestamp, engagement_status = ENGAGED
Demo · System Loop
Open every demo with this — set the frame
0:00 – 0:30
What this is (and what it isn't)
SAY
"This is not a CRM setup and it's not a funnel. Those are static. This is a closed loop — it takes in leads, scores their behaviour in real time, routes them automatically, detects when things slow down, and fixes them without anyone having to notice."
SHOW
Navigate to Phase 00 → Map. Point at the 11-step loop from top to bottom. Move slowly — let each label land.
SAY
"Every single action a lead takes — every email open, SMS reply, call — adds to an intent score. That score decides what happens next. Not a human. The system."
IF ASKED
"What if the system makes the wrong call?" → "Every automated decision has a human override. The system suggests and acts, but a rep can always take control. Human override always."
Phase 01 · Foundation
System Foundation & Non-Negotiable Build Order
Everything that must exist before a single Zap fires. Fields before pipeline. Pipeline before sequences. Sequences before Zaps. Zaps before you add messages. Break this order and you rebuild twice.
Day 1–276 fields9 stages22 sequences24 Zaps
Full tech stack — v8
GHLCRM hub · $97–297/mo · Day 1 non-negotiable. All contacts, pipelines, sequences, tasks. Single source of truth — every other tool writes back here. Starter → Pro at >1 rep.
ZapierProfessional ($49/mo) minimum. 24 Zaps. Starter/Core plans cannot run scheduled Zaps (12,15,17,18,22,23,24) or multi-step flows. See Phase 24 for Make/n8n/ActivePieces alternatives at lower cost. Do not start on a free or Starter plan — you'll rebuild everything when you upgrade.
CalendlyBookings · Teams plan ($16/mo) for collective scheduling. Consultation event + one Site Visit event per estimator.
Bland AIAI outbound calling · ~$0.09/min · Path 1 only. Pathway configuration (script + disposition map) MUST be complete before Zap 5 is activated. Never activate Zap 5 with an unconfigured pathway — it will call real leads with a blank script. Test on your own number minimum 10 times before go-live.
PandaDocE-sign · Business plan ($49/mo) for payment + CPQ. Estimates + contracts + invoices + change orders + welcome packets.
StripePayments · 2.9%+$0.30 · payment.cleared is the ONLY Phase 3 trigger. Not payment.submitted. Webhooks via Zapier.
Google SheetsData layer · Free · Master Logs (8 tabs) + Territory Map + Materials DB + Schedule + Sub Directory + Compliance Calendar.
Looker StudioReporting · Free · Build LAST. 7 dashboards: Funnel, Marketing, Sales, Ops, Finance, AI, After-sales.
GA4 + GTMTracking · Full event taxonomy (11 events). UTM capture on all form fields. Server-side via CAPI.
Meta CAPIServer-side conversions · Fire Lead on form submit + Purchase on deposit.cleared. Closes ad attribution loop.
CallRailCall tracking · $45/mo · One tracking number per channel (Website / Google Ads / GMB / Facebook). Webhooks → GHL.
Xero / QuickBooksAccounting · Sync via Zapier. Invoices, payments, reconciliation. Bridge: GHL invoice events → Xero. Critical Month 1.
BuildertrendProject management + field crew. Phase updates → GHL field updates. Daily log → PM SMS content. Schedule delays → Slack. Month 1. Pro plan required for webhooks — Zaps 19–21 will not function on Basic plan.
CompanyCamPhoto documentation · $49/mo · 3+ estimators. Project milestones → GHL note with gallery link. Site visit photos auto-organized.
SlackInternal alerts · Free OK · #high-value-leads / #manager-approvals / #project-alerts / #finance-alerts channels.
ZeroBounceEmail validation · API call in Zap 1 · Real-time — no invalid emails ever enter sequences.
9-stage pipeline — exact names (never rename)
01
Lead In
⚡ Zap 1
02
Nurturing
⚡ WF-2,3
03
Stalled
⚡ WF-2
04
Appt Scheduled
⚡ Zap 3
05
Proposal Sent
🔐 Zap 9
06
Negotiation
Rep
07
Closed Won
⚡ Zap 7
08
Closed Lost
→ Exit
09
Archive
⚡ Zap 8
Build · Foundation
Day 1 checklist — GHL only
STEP 1
GHL Business Settings — before any field
Go
GHL → Settings → Business Info
Business Name · Address · Phone · Timezone · Business Hours · Reply-to email.
GotchaWrong timezone = sequences fire at 3am. Set this before creating a single field.
STEP 2
Create all 76 custom fields — see Phase 21 for full dictionary
1
GHL → Settings → Custom Fields → Add Field. Create in phase order.
// Create in this exact order — later fields depend on earlier ones:
// 1. System fields: lifecycle_stage, engagement_status, intent_score, first_response_time, last_activity_timestamp
// 2. Marketing fields: utm_source through ad_creative_id (16 fields)
// 3. Phase 1 fields: lead_score, budget, timeline, path_assigned etc (8 fields)
// 4. Phase 2 fields: booking_sequence through loss_reason (7 fields)
// 5. Phase 3 fields: contract through final_payment_date (12 fields)
// 6. Finance fields: material_cost through change_order_3_amount (19 fields)
// 7. Scheduling + snag + survey + warranty fields (remaining)
CriticalEvery field must exist before Zapier can write to it. Build Zap 1 before creating lead_score → Zap errors on every run.
STEP 3
Create 9-stage pipeline + 22 sequence shells (names only)
1
Pipeline: GHL → Pipelines → "ASMC Main Pipeline"
Lead In → Nurturing → Stalled → Appointment Scheduled → Proposal Sent → Negotiation → Closed Won → Closed Lost → Archive
2
Sequences: GHL → Automations → Create Workflow → name only, no steps
// Path nurture (angle-based):
Comfort Nurture · Home Value Nurture · Lifestyle Nurture · Protection Nurture
// Standard paths:
Urgent Nurture · Premium Education · Long Cycle · Standard Nurture
// Behavioral:
Price Objection · Social Proof · SMS Objection · Estimate Follow-Up
// Booking:
Site Visit Confirmation · Pre-Construction Welcome
// Post-sale:
Contract Win · Lost Deal Exit · 12-Month Passive Nurture · Past Client 24-Month
// Specialist:
Warranty Expiry · Review Follow-Up · Lost Price Track · Lost Timing Track
TipAdd messages on Day 3 after Zaps 1 and 3 are verified. Today shells exist so Zapier can map to them by name.
Demo · Foundation
Show the architecture before showing anything live
0:30 – 1:00
The structure — before any live demo
SAY
"Before I show anything live — here's the architecture. 76 custom fields. 22 nurture sequences. 24 automation Zaps. 9 pipeline stages. Every single tool writes back to the same contact record."
SHOW
Navigate to Phase 21 (Fields Dictionary). Scroll the table slowly. Don't explain each one — let density speak.
SAY
"Each field has an API key, a data type, and exactly one thing that writes to it. That's how the data stays clean. No duplicates. No ambiguity."
Phase 02 · Marketing Engine
Paid Traffic Architecture — Non-Leaky
The current system has no campaign structure, no creative testing framework, no budget model, and no retargeting architecture. This phase documents the complete paid traffic machine. Every dollar of ad spend is tracked from impression to contract. Nothing leaks.
Revenue-criticalMeta + Google3 campaignsCPL → contract
The 4 creative angles — each is a separate universe
AngleCore fear/desireHook frameLP headline style
ComfortHome doesn't feel like home anymore"The backyard you never use — what if you did?"Transform how you feel about coming home
Home ValueProperty not performing as investment"Homeowners in [suburb] added $42K with this project"Add $X–$Y to your property value before you list
LifestyleNot living the life they imagined in this home"The outdoor space you planned when you bought the house"Build the space you actually use — every single day
ProtectionWeather / wear / neglect is a ticking clock"Every season you wait costs more to fix"Stop the damage before it becomes a $40K repair
Full Meta campaign architecture
CAMPAIGN 1
PROSPECTING — 75% of total budget
Objective
Leads (optimise for form submit events)
Ad Set 1A
Interest targeting: home renovation, outdoor living, property value, interior design, landscaping. Radius: service area. HHI filter if available.
Ad Set 1B
Broad + Meta Advantage+: let the algorithm find buyers. Run alongside 1A to test — broad often wins at scale.
Ad Set 1C
Lookalike 1% from past clients — uploaded monthly from GHL export. Most efficient CPL of any prospecting set.
Creative
3 ads per set × 4 angles = 12 prospecting ads minimum. Each ad → matching LP → matching sequence. No angle mixing.
Bidding
Cost cap on CPL target. Start with lowest cost until 50+ leads → then set cost cap.
CAMPAIGN 2
RETARGETING WARM — 20% of budget
Ad Set 2A
LP visitors 14d (excluded form submitters): these people showed intent but didn't commit. Hit them with social proof + urgency.
Ad Set 2B
75%+ scroll on LP, last 7d: the hottest audience in the funnel. They read everything and still didn't submit. Overcome their specific objection.
Ad Set 2C
GHL email opens (export weekly + upload as Custom Audience): people who opened nurture emails but haven't booked. Show them a project close to their ZIP.
Creative
Before/after project photos. Video testimonials (30s). Review screenshots. "Just completed a project in [suburb]" posts. Urgency: "2 slots left in [month]."
Bidding
CPM (not CPC). These audiences are small and warm — pay for impressions, not clicks.
CAMPAIGN 3
RE-ENGAGEMENT — 5% of budget
Ad Set 3A
Past clients 180d+: they already trust you. Upsell next phase, expand project, or ask for referral. "Your neighbors are asking about what you did."
Ad Set 3B
Closed-lost contacts (exported from GHL, loss_reason ≠ NotQualified): D90 win-back in ad form. "How did your project go with the other company?"
Google Ads — bottom-funnel demand capture
Campaign typeKeywords / targetingMatch typeBudget split
Search — BrandCompany name + variationsExact5% · protect from competitor bidding
Search — Service"patio contractor [city]" · "deck builder near me" · "fence installation [suburb]" · "[service] company [city]"Phrase + Exact60% · highest intent
Search — Problem"fence falling down" · "deck needs replacing" · "outdoor renovation ideas"Broad + negative list20%
GDN RetargetingGA4 audience: LP visitors no convertDisplay15%
Creative testing framework
Test cadence3 hooks per angle per quarter = 12 new creatives every 90 days
Variable order1st test: Hook text. 2nd test: Hero image. 3rd test: CTA button. 4th test: Offer. Test one variable at a time.
Win ruleCPL < target AND avg lead_score > 15 after 300+ impressions AND at least 10 leads → promote, scale budget 20%
Kill ruleCPL > 2× target after 500 impressions → kill. No exceptions.
Full-funnel ruleCPL alone is not the metric. Track CPL → avg lead_score → booking_rate → contract_rate. A cheap CPL from low-quality leads costs more than an expensive one from high-quality leads.
Tracking fieldad_creative_id passed via UTM content → stored on GHL contact → Looker "Creative Performance" report
Offer architecture — "Get a free quote" is not an offer
Primary offerFree 3D Design Concept OR Property Value Impact Report OR 10-Year Cost Comparison — one per angle. High-perceived-value deliverable, not a quote.
Secondary offer"Book your design consultation — only [X] spots available in [month]." Capacity urgency is real and credible for construction.
Urgency (real)Seasonal: "Book before [date] to guarantee [season] start." Pricing: "Lock in current pricing — materials increase Q[X]." Capacity: "We take [X] new projects/month."
Risk reversal"If you don't love the design concept, you owe us nothing." Removes the single biggest barrier to form submission.
DeliveryInstant on form submit. Zap 1 fires lead magnet email within 2 minutes. Never "we'll get back to you" — deliver the offer immediately.
Build · Paid Traffic
Week 1 — campaign setup
META SETUP
Campaign build order — do not run all at once
1
Week 1: Build 4 landing pages (one per angle). Verify pixel fires on each. Verify UTM params pass through.
2
Week 1: Build Campaign 1 only. 3 ad sets, 2 creatives per set. Set CPL goal. Run for 2 weeks minimum before optimising.
3
Week 3: Add Campaign 2 retargeting once pixel has enough data (need 100+ LP visitors to build audiences).
4
Month 2: Add Campaign 3 once you have past client + closed-lost lists of 100+ each to upload.
5
Google Ads: Start with Search — Service campaign only. Brand + Problem campaigns added in Month 2.
6
Creative testing tracker — Google Sheet columns:
creative_id · angle · hook_text · format · cpl · impressions · leads · avg_lead_score · bookings · contracts · contract_rate · status
// Update weekly. Sort by contract_rate not CPL.
Demo · Paid Traffic
Show the anti-leak system
1:00 – 1:45
Why most ad budgets leak — and how this doesn't
SAY
"Most construction companies run one ad, one landing page, one form. Every lead — comfort buyer, investment buyer, protection buyer — sees the same thing. This converts maybe 2-3% of clicks. This system runs 4 separate universes, each speaking to a specific fear or desire."
SHOW
Navigate to Phase 02 → Map. Point at the 4 angles table. Then point at the 3-campaign structure.
SAY
"75% goes to cold prospecting. 20% to warm retargeting — the people who visited the landing page and didn't submit. Those people are close. They just need to see the right creative one more time. The other 5% re-engages past clients."
Phase 03 · Marketing Engine
Tracking & Attribution — Full Closed Loop
Without attribution, ad spend is a guess. This phase closes the loop from impression all the way to contract value — so you know exactly which campaign, angle, and creative generated a $40K job vs an $8K job.
Revenue-criticalGTM · GA4 · CAPI11 eventsFirst + last touch
Full UTM taxonomy — all 7 parameters
ParameterField in GHLValuesExample
utm_sourceutm_sourcefacebook / google / instagram / organic / referral / emailfacebook
utm_mediumutm_mediumcpc / cpm / email / social / callcpc
utm_campaignutm_campaignprospecting_comfort / retargeting_warm / reengagement_lostdealsprospecting_comfort
utm_contentad_creative_idcreative_id (matches creative tracker sheet)comfort_before_after_v3
utm_termutm_termGoogle Ads keyword (auto-populated for search)deck+builder+near+me
utm_anglelead_anglecomfort / home-value / lifestyle / protectioncomfort
first_touchfirst_touch_sourceCaptured on first form submit — never overwrittenfacebook/cpc/prospecting_comfort
last_touchlast_touch_sourceOverwritten on every new form submit or re-entrygoogle/cpc/retargeting_warm
Full GA4 event taxonomy — all 11 events
Event nameTriggerKey parameters
page_viewAll pages — GTM autopage_path, page_title
lp_viewedLanding pages only — GTM URL filterlp_angle, utm_source, utm_campaign
form_startedFirst field interaction on formform_name, lp_angle
form_submittedForm submit (Zap 1 trigger)budget, timeline, lead_score, utm_campaign
lead_scoredAfter Zap 1 Code step — server-sidelead_score, path_assigned, lifecycle_stage
booking_createdAfter Zap 3 firesbooking_date, assigned_rep, lead_score
estimate_sentestimate_amount field updatedestimate_amount, estimate_option
contract_signedZap 7 fires (PandaDoc webhook)contract_value, utm_source (first touch)
deposit_clearedStripe payment.cleareddeposit_amount, pm_assigned
project_completeZap 8 fires (final_payment_date set)job_revenue, actual_margin_pct
referral_submittedReferral form submitreferrer_id, referral_angle
Meta CAPI — close the attribution loop server-side
Why CAPI mattersiOS14+ blocks ~40% of pixel events. CAPI sends conversions directly from your server — no browser required. Without it, Meta undercounts conversions and overspends.
Event: LeadFire on form_submitted. Include: email (hashed), phone (hashed), lead_score, utm_campaign, utm_content. This tells Meta the quality of the lead, not just that a form was submitted.
Event: PurchaseFire on contract_signed (Zap 7). Value = contract_value. This closes the attribution loop — Meta now knows which ad generated a $40K contract.
DeduplicationUse event_id = GHL contact_id + event_name + timestamp. Without deduplication, browser pixel + CAPI will double-count every event.
Match rate targetAim for >70% match rate in Meta Events Manager. Below 50% = add more PII parameters (first_name, last_name, city, ZIP).
Pre-lead pixel events — capture the 97% who don't submit
GTM triggerMeta Pixel eventRetargeting audience built
LP page loadViewContentLP visitors last 30d
Scroll 75%Custom: scroll_75High-intent non-converters last 14d (hottest)
CTA button clickInitiateCheckoutButton clickers last 7d (closest to converting)
Video 50% watchViewContent (video)Video watchers last 21d
Time on page 60sCustom: engaged_readerHigh-engagement visitors
Form submitLeadForm submitters last 180d (for suppression + re-engagement)
Build · Tracking
GTM + GA4 + CAPI setup
GTM SETUP
DataLayer push structure for all 11 events
1
Install GTM container on all LP pages + website. Verify GTM preview shows correctly before publishing.
2
Form submit dataLayer push (add to form's success callback or GHL embed):
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'form_submitted',
lead_score: '{{lead_score}}', // from hidden field populated by URL param
budget: '{{budget}}',
timeline: '{{timeline}}',
lead_angle: '{{lead_angle}}',
utm_source: '{{utm_source}}',
utm_campaign: '{{utm_campaign}}',
utm_content: '{{utm_content}}'
});
3
In Zap 1: after contact created, fire a server-side event to Meta CAPI endpoint with hashed PII + event_id for deduplication.
4
In Zap 7 (contract signed): fire Meta CAPI Purchase event with contract_value. This is the money event.
Revenue attributionIn Looker Studio: join lead_log (utm_source + utm_campaign) with completion_log (actual_amount) on contact_id → "Revenue by Campaign" chart. This tells you which campaign generated which revenue — not just which campaign generated leads.
Demo · Tracking
Show the closed loop — from ad to contract
Proof point
Show revenue attribution, not lead attribution
SAY
"Most businesses know their cost per lead. This system knows the revenue per campaign. We can tell you that Facebook Comfort Angle generated $180,000 in contracts last quarter at a cost of $2,400. That's a 75× return on ad spend. You cannot make that calculation without this attribution layer."
Phase 04 · Marketing Engine
Organic & SEO — Single-Point-of-Failure Insurance
100% paid dependency is a single point of failure. Meta bans your account → zero leads. This phase builds the organic infrastructure that runs in parallel, compounds over time, and costs only labor. A GMB photo posted today generates leads 3 years from now.
Risk mitigationCompounds over timeGMB · SEO · YouTube
Google Business Profile — the highest-ROI organic channel
SetupAll categories listed · All services listed · All products (with prices) listed · 10+ photos minimum at launch · Q&A section seeded with 10 common questions (answered by you, not Google).
Weekly postsEvery completed project → GMB post with before/after photo + "[project type] just completed for a [suburb] homeowner." 150–300 words. Geotagged photos when possible.
Review response SOPEvery review (positive or negative) responded to within 48 hours. 5-star: "Thank you [name] — it was a pleasure working on your [project]." 1-star: Never defensive. Acknowledge → apologize → offer to make it right → take offline.
GHL integrationWhen project_status = complete → create task "POST TO GMB — {{first_name}} project — attach before/after photos from CompanyCam." Set gmb_posted = true when done.
Local SEO pages — city × service matrix
Page structureOne page per [service] × [suburb]. URL: /deck-builder-[suburb]/ · Title: "Deck Builder in [Suburb] | [Company Name]" · 800+ words. Each page has its own form (same Zap 1 endpoint).
Priority orderBuild pages for highest-revenue service first × highest-population suburbs first. 2 pages/week = 24 pages in 3 months.
Content formulaIntro (what you do in this suburb) → process (how you work) → gallery (projects in this suburb or nearby) → testimonials → pricing guide (ranges only) → CTA → FAQ (10 questions, keyword-rich).
Internal linksEvery city/service page links to: the main service page, 2-3 related service pages, and the main contact/form page.
YouTube — before/after project videos (ranks locally)
Video format60–90 seconds · Title: "[Project Type] Transformation in [Suburb] — Before and After" · Always include suburb in title and description.
Capture SOPEstimator takes 60s walkthrough on phone at site visit (before). PM takes 60s walkthrough at completion (after). Sent to editor or assembled in CapCut in 20 minutes.
GHL integrationAt site visit confirmed: task fires to estimator "Capture before walkthrough video." At project_status = complete: task fires to PM "Capture completion walkthrough video." Fields: video_captured (Boolean).
Description formulaFirst 2 lines: keyword-rich description of the project. Then: "We recently completed this [project] for a homeowner in [suburb]. [2-3 sentences about scope]. Ready to transform your outdoor space? Link to LP."
Organic social — 3 posts/week minimum
DayPost typePlatformContent
MondayProject showcaseInstagram + FacebookBefore/after carousel. Tag suburb. Caption tells a short story.
WednesdayEducationalFacebook + Instagram"3 questions to ask before hiring a [contractor type]" OR "[project type] buying guide." Builds authority.
FridaySocial proofAll platformsScreenshot or graphic of a 5-star review. Client quote. Always tag suburb.
Build · Organic
Month 1 organic setup
ORGANIC SETUP
Week-by-week organic launch sequence
1
Week 1: GMB full setup. Add 10+ photos. Seed 10 Q&As. Verify phone number. Connect to GA4.
2
Week 2: Write first 3 city/service SEO pages. Submit sitemap to Google Search Console.
3
Week 3: Record first 3 project walkthrough videos (before+after from existing projects). Publish to YouTube.
4
Week 4: Set up social posting schedule. Create content calendar in Google Sheets (date, platform, post type, content, status).
5
Ongoing: Every project completion → GMB post + YouTube video + 2 social posts. System runs on project output.
Demo · Organic
The compound asset — every project generates future leads
Proof point
One video from 3 years ago still generates leads today
SAY
"Every completed project generates a before/after video, a GMB post, and 2 social posts. That content compounds. A video we posted 18 months ago about a deck build in [suburb] still ranks #2 on Google for 'deck builder [suburb]' and generates 3-4 leads a month. Zero ongoing cost."
Phase 05 · Marketing Engine
Email Architecture — Deliverability + Broadcast System
The current system only has triggered sequences. Without a broadcast strategy, seasonal campaigns, and deliverability infrastructure, the entire email system is fragile. One domain issue kills everything. This phase builds the infrastructure that keeps emails in inboxes, not spam folders.
Deliverability-criticalSPF · DKIM · DMARCBroadcast calendarList hygiene
Deliverability infrastructure — must be in place before any sequence fires
SPF recordAdd v=spf1 include:sendgrid.net include:mailgun.org ~all to DNS. Prevents spoofing. Required for inbox placement.
DKIMEnable in GHL → Settings → Email Services. Provides cryptographic proof emails are from you. Google rejects unsigned emails.
DMARCStart with p=none (monitoring only). Move to p=quarantine after 30 days clean data. Blocks spoofed emails sent in your domain's name.
Sending domainUse a subdomain for sending: mail.yourcompany.com NOT yourcompany.com. If sending domain gets flagged, website domain stays clean.
Warm-up protocolNew sending account: Day 1–7 max 50 emails/day → Week 2 max 200/day → Week 3 max 500/day → Week 4+ normal volume. Never jump to bulk volume on a fresh domain.
Postmaster monitoringGoogle Postmaster Tools + Microsoft SNDS. Check weekly. Spam rate alert: if >0.1% → pause sending and investigate immediately.
Email validation — before any contact enters a sequence
ZeroBounce APICalled in Zap 1 Step 0.5 — before contact is created. If result = invalid/catch-all/disposable → tag Status:InvalidEmail → do NOT add to email sequences → SMS only.
email_status fieldDropdown: active / cold / bounced / DNC / unsubscribed / invalid. Updated automatically by email events.
Quarterly cleanExport all contacts with email_status = cold (no open 60d) → run through ZeroBounce bulk verify → remove invalid addresses from GHL.
Broadcast email calendar — minimum viable schedule
MonthBroadcast typeSegmentSubject line style
JanuaryNew year project planningAll leads in Nurturing"Is this the year you finally do it?"
MarchSpring booking pushAll leads + past clients"Spring slots are filling — here's what's left"
MayCapacity urgencyMOFU leads + stalled"We have 2 project slots in June — want one?"
AugustFall booking pushAll leads"Book now — complete before the holidays"
OctoberPre-winter protectionProtection angle leads"Before the first freeze — here's what can't wait"
DecemberLock in next year pricingAll leads + past clients"2025 pricing locked if you book this month"
MonthlyProject showcase newsletterAll active contacts"What we built in [month] + [tip]"
List hygiene — ongoing
Engagement segmentsSmart List A: Active (opened email last 30d). Smart List B: Warm (opened last 31–90d). Smart List C: Cold (no open 90d+). Only A+B receive broadcasts. C receives re-engagement SMS only.
Re-engagement flowCold 90d → SMS: "Still thinking about your project? Reply YES and we'll reach back out." YES → move to MOFU + rep task. No reply 7d → move to Passive Nurture. No reply 30d → tag Status:Cold.
Suppression listExport DNC + bounced + unsubscribed monthly → import as suppression into any other email tools (Klaviyo, Mailchimp if used). Cross-platform suppression is legally required under CAN-SPAM/GDPR.
SMS broadcast architecture
TCPA complianceExplicit opt-in on form: "I agree to receive SMS messages from [Company]. Msg & data rates may apply. Reply STOP to opt out." Store sms_consent_timestamp.
Keyword responsesSTOP → immediate unsubscribe + tag SMS:OptedOut. HELP → auto-reply with support number. YES → confirmation trigger in Zap.
Neighborhood SMSAfter project completion (Zap 8): build Smart List of contacts within same ZIP ±2 codes. Broadcast SMS: "Just finished a [project_type] in your neighborhood — want to see the photos?" Links to GMB.
Build · Email Architecture
Week 1 — deliverability first, content second
Critical orderSet up SPF, DKIM, DMARC BEFORE you send a single email. One week of high spam complaints from an unconfigured domain can permanently blacklist you. DNS changes first. Sequences second.
Demo · Email
Show the infrastructure behind the sequences
Proof point
Why most construction company emails go to spam
SAY
"Every email you send is being evaluated before your prospect even sees it. SPF, DKIM, DMARC — these are the authentication records that tell inbox providers you are who you say you are. Without them, you're sending from an unverified domain and your open rates will be 8% instead of 35%."
Phase 06 · Marketing Engine
Lead Capture — Zap 1, Scoring, Angle Routing
Every lead enters here. Zap 1 validates the email, calculates the intent score, assigns the angle-specific nurture path, fires Bland AI on Path 1, and logs to Sheets — all within 90 seconds of form submit.
Zaps 1,2,4,55 pathsAngle routingZero dead leads
Scoring matrix
Budget: Premium+10 pts · Path 1 or 2
Budget: Standard+5 pts
Budget: Starter+2 pts
Budget: Unknown0 pts
Timeline: Urgent+15 pts
Timeline: Near+10 pts
Timeline: Long+5 pts
Timeline: Researching+2 pts
Source: Referral bonus+8 pts added to base score
5 lead paths → angle-specific sequences
Path 1 · 21–25Priority:Highest · Senior rep · Bland AI <5min · Angle Nurture (same-day) · Urgent Nurture (D1)
Path 2 · 16–20High Priority · Rep call task 4hr · Angle Nurture + Urgent Nurture
Path 3 · 11–15Angle Nurture → Premium Education 12d
Path 4 · 6–10Angle Nurture → Long Cycle 21d → 12-Month Passive
Path 5 · 0–5Angle Nurture → Standard Nurture 30d → 12-Month Passive
Angle → message alignment — the entire system must speak the same language
Anglelead_angle_opener valueFirst email subjectRep call opener
comfortmaking your home a place you never want to leave"Your request about transforming how you feel at home""You mentioned you want your home to feel different — tell me what that looks like for you."
home-valueprotecting and growing your biggest asset"Your property value question — here's what the data says""You're thinking about this from an investment standpoint — smart. What's your timeline on the property?"
lifestylebuilding the outdoor space you actually use"The outdoor space you planned when you bought this house""You said you're not using your outdoor space the way you imagined — what does it look like right now?"
protectionmaking sure your home is ready for what comes next"The issue you flagged — here's what we see most often""You reached out because something's not right — tell me what you're seeing."
Build · Lead Capture
Zap 1 — the most important Zap in the system
ZAP 1
Form submit → validate → normalize → score → angle → path → sequence → log
Build first. Test with real submit, not Zapier sample data.
T
Trigger — Webhooks by Zapier: Catch Hook
// Form fields required (exact names, case-sensitive):
first_name last_name email phone
budget // Premium | Standard | Starter | Unknown
timeline // Urgent | Near | Long | Researching | Unknown
lead_angle // comfort | home-value | lifestyle | protection
zip_code // required for territory routing
project_type // deck | patio | fence | pergola | pool | landscaping | other
sms_consent // true | false — TCPA compliance
utm_source utm_medium utm_campaign utm_content
0.5
Action — ZeroBounce: Validate email
If result = invalid/disposable/catch-all → set email_status=invalid → skip email sequences → SMS only. Do NOT create a contact with a bad email.
1
Action — GHL: Find Contact by Phone (dedup check)
If found by phone with different email → update record, do NOT create duplicate. Same phone = same person.
2
Action — GHL: Create/Update Contact (Update mode always)
first_name last_name email phone lead_angle budget timeline zip_code project_type
utm_source utm_medium utm_campaign ad_creative_id {{utm_content}}
sms_opt_in {{sms_consent}}
lifecycle_stage TOFU
engagement_status NEW
3
Action — Code by Zapier (JavaScript): Score + path + angle opener + first_touch
let score = 0;
const b = {Premium:10, Standard:5, Starter:2};
const t = {Urgent:15, Near:10, Long:5, Researching:2};
score += b[inputData.budget] || 0;
score += t[inputData.timeline] || 0;
if (inputData.lead_angle === 'referral') score += 8;
const path = score>=21?'path1':score>=16?'path2':score>=11?'path3':score>=6?'path4':'path5';
const openers = {comfort:'making your home a place you never want to leave',
'home-value':'protecting and growing your biggest asset',
lifestyle:'building the outdoor space you actually use',
protection:'making sure your home is ready for what comes next'};
const seqMap = {path1:'Urgent Nurture',path2:'Urgent Nurture',path3:'Premium Education',path4:'Long Cycle',path5:'Standard Nurture'};
const angleSeq = inputData.lead_angle.charAt(0).toUpperCase()+inputData.lead_angle.slice(1)+' Nurture';
output = [{score, path, opener:openers[inputData.lead_angle]||'', seq:seqMap[path], angleSeq}];
4
Action — GHL: Update Contact (score, path, opener, first_touch)
lead_score {{score}}
path_assigned {{path}}
intent_score {{score}} // seeds the intent engine
lead_angle_opener {{opener}}
first_touch_source {{utm_source}}/{{utm_campaign}} // only if empty
last_touch_source {{utm_source}}/{{utm_campaign}} // always overwrite
Pipeline Stage Lead In
timestamp_created {{now}}
5
Action — GHL: Apply Tags
Budget:{{budget}} Timeline:{{timeline}} Source:{{lead_angle}} Path:{{path}}
// If sms_consent = true: SMS:Opted-In
// If sms_consent = false: SMS:OptedOut
6
Action — GHL: Enrol in BOTH sequences (angle-specific FIRST, then path-based)
// Sequence 1 (fires immediately — angle-specific):
{{angleSeq}} // e.g. "Comfort Nurture" — opens with angle language
// Sequence 2 (fires D1 — path-based):
{{seq}} // e.g. "Urgent Nurture" — path-specific timing
7
Action — Google Sheets: Append to lead_log
timestamp · contact_id · name · email · phone · lead_angle · budget · timeline · score · path · utm_source · utm_campaign · ad_creative_id · zip_code · project_type
8
Action — Meta CAPI: POST Lead event (server-side)
event_name Lead
event_id {{contact_id}}_lead_{{timestamp}} // dedup key
email SHA256({{email}})
phone SHA256({{phone}})
custom_data {lead_score: {{score}}, path: {{path}}}
Demo · Lead Capture
Live form submit — watch the system react
1:45 – 2:30
Submit a lead — show what happens in 90 seconds
SAY
"I'm going to submit a form right now. In the next 90 seconds: the email will be validated, a lead score will be calculated, the contact will be created in GHL with all 8 fields populated, it'll be tagged, enrolled in 2 sequences, logged to Sheets, and a CAPI event will fire to Meta."
DO
Submit test form. Budget=Premium, Timeline=Urgent, Angle=Comfort. Switch to GHL contacts. Refresh. Show the new contact — score=25, path=path1, angle_opener populated, tags applied, sequences enrolled.
IMPACT LINE
"That entire process happened without anyone doing anything. And 97% of your competitors are doing that manually — or not doing it at all."
Phase 07 · Sales Machine
Consultation & Booking — Zap 3 + Call SOP
Every booking is a structured handoff. Zap 3 creates the contact note, the prep task, and the Slack alert. The rep goes into every call knowing the lead's score, angle, budget, timeline, and the right opening line. No cold calls in this system.
Zaps 3,4,5Angle-primed callsSLA enforced
Consultation call SOP — 20-minute structure
MinutePhaseWhat the rep does
0:00–1:00Open with the angleUse {{lead_angle_opener}} in first sentence. "I saw you were thinking about [angle opener] — tell me more about what's going on at the house."
1:00–7:00Discovery — listen 80%Questions: What does the space look like now? What needs to change? Who else uses this space? What's driven you to look at this now? Have you had anyone else look at it? What happened with that?
7:00–12:00Pain + motivation depth"If nothing changed in the next 12 months — what would that cost you?" (comfort/lifestyle leads). "What do you think this is doing to your property value right now?" (home-value leads).
12:00–17:00Solution framingPresent the process — not the product. "Here's how we approach this..." Walk through site visit → design → estimate → timeline. Never name a price on this call.
17:00–20:00Next step close"Based on what you've shared, I'd like to have one of our design specialists visit the site. We have [day] or [day] available this week — which works better?" Offer 2 options only. Never ask "when's good for you?"
Zap 3 — Calendly → GHL full handoff
ZAP 3
Calendly consultation booked → GHL update + briefing + prep task + Slack
T
Trigger — Calendly: Invitee Created (Consultation event only)
1
Action — GHL: Find Contact by Email → if not found, create
2
Action — GHL: Update Contact
booking_date {{event_start_time}}
booking_sequence 1_consultation
Pipeline Stage Appointment Scheduled
lifecycle_stage BOFU
intent_score {{current}} + 5 // booking is +5
engagement_status ENGAGED
entered_stage_timestamp {{now}}
3
Action — GHL: Add Note (rep briefing package)
// Note template (all fields pre-populated):
📋 BRIEFING — {{first_name}} {{last_name}}
Score: {{lead_score}} | Path: {{path_assigned}} | Angle: {{lead_angle}}
Budget: {{budget}} | Timeline: {{timeline}} | Project: {{project_type}}
ZIP: {{zip_code}} | Source: {{utm_source}}/{{utm_campaign}}

🎯 OPEN WITH: "{{lead_angle_opener}}"
📌 CALL OBJECTIVES: Discovery → Pain depth → Site visit close
4
Action — GHL: Create Task (call prep)
Title PREP — {{first_name}} — Score:{{lead_score}} — {{booking_date}}
Due {{booking_date}} − 1 hour
Note See contact note for full briefing. Open with: "{{lead_angle_opener}}"
5
Filter: if lead_score ≥ 16 → Action: Slack alert to #high-value-leads
🔥 HIGH-VALUE BOOKING — {{first_name}} {{last_name}}
Score: {{lead_score}} | Budget: {{budget}} | Timeline: {{timeline}}
Booked: {{booking_date}} | Rep: {{assigned_rep}}
Source: {{utm_source}}/{{utm_campaign}}
6
Action — Google Sheets: Append to booking_log
timestamp · contact_id · name · booking_date · rep · lead_score · lead_angle · utm_source
Build · Consultation
Zap 3 build notes
CriticalAdd "create if not found" to the GHL Find Contact step. If the rep manually scheduled the call outside Calendly, the contact won't exist in GHL and the Zap will error silently.
Demo · Consultation
Show the rep briefing note
2:30 – 3:00
The rep walks into every call knowing everything
SAY
"The moment someone books a consultation, a briefing note appears in their GHL contact. Score, angle, budget, timeline — and the exact opening line the rep should use based on which ad this person clicked. No cold calls. Every call is a warm, prepared conversation."
Phase 08 · Sales Machine
Site Visit & Estimation — Territory + Pricing + Good/Better/Best
The site visit is where the sale is decided, not the estimate presentation. The estimator who walks in prepared, documents thoroughly, and presents Good/Better/Best wins 2× more than one who shows up and guesses. This phase is the estimator's full operating system.
Zap 6,13Territory routingGBB pricingNo-show handling
Site visit SOP — before, during, after
PhaseWhat the estimator must do
Before (24hr)Read GHL contact note → know the angle, budget, project type, what was promised on the consultation call. Pull up any existing case studies matching their project type.
Before (1hr)Confirm with client via SMS: "Just confirming I'll be there at [time]. Any gate codes or parking notes?" Reply = engagement confirmation.
On site — startTake 60-second before walkthrough video on phone (for YouTube + CompanyCam). Reference the angle: "You mentioned [comfort/value/lifestyle/protection] — walk me through the space from that perspective."
On site — measureAll measurements documented in CompanyCam or Google Drive. Photo naming: YYYY-MM-DD_ClientName_Measurement_[location]. Every corner. Every obstacle.
On site — closeAt the end: "I have everything I need to put together 3 options for you — I'll have those to you within 48 hours. Before I go — is there anything else about the space or the project that I should factor in?"
After (2hr)Update site_visit_outcome in GHL. Upload photos to CompanyCam/Drive. Update measurements_folder URL. Start estimate.
Estimate: Good / Better / Best — always present 3 options
OptionWhat it includesPricingPurpose
A — StandardFunctional scope, standard materials, base warranty. Minimum acceptable quality for your brand.Your minimum job size. Never discount from this.Anchors the client upward. Most won't take this.
B — PremiumFull scope, upgraded materials, extended warranty, priority scheduling. This is the real recommendation.+25–35% above Option AThe one you want them to choose. 70% will.
C — LuxuryFull scope + all upgrades + custom features + premium materials. Everything possible.+50–70% above Option AMakes Option B look reasonable. Some will take it.
Zap 6 — territory routing + estimator assignment
ZAP 6
Site visit booked → ZIP → territory lookup → estimator assigned → confirmation
T
Trigger — Calendly: Invitee Created (Site Visit event only)
1
Action — Google Sheets: Lookup territory_map by ZIP code
Returns: estimator_name, estimator_phone, estimator_calendly_link, status (Active/Out/Coverage), weekly_capacity.
F
Filter: if status = Out OR no match → create manual task "Assign estimator — ZIP {{zip_code}}" → assign to manager. Stop Zap here.
2
Capacity check: count this estimator's bookings this week in booking_log
If bookings ≥ weekly_capacity → route to next available estimator by same lookup (status = Active, fewest bookings this week).
3
Action — GHL: Update Contact
estimator_assigned {{estimator_name}}
site_visit_date {{event_start_time}}
booking_sequence 2_site_visit_confirmed
4
Action — GHL: Enrol in Site Visit Confirmation sequence (confirm + D-1 reminder + 2hr SMS + estimator phone)
ZAP 13
No-show detection — site_visit_date + 45min → outcome check
T
Trigger — Delay: scheduled 45 minutes after site_visit_date
F
Filter: if site_visit_outcome is still empty (not yet set by estimator)
→ Send SMS to client: "Hi {{first_name}} — just checking in, are you still available for {{estimator_assigned}}'s visit today?" → Create rep task "Check on no-show — {{first_name}}"
2
If 2nd no-show (estimator_no_show_count ≥ 2) → tag Status:NoShow → move to Stalled → rep task "2nd no-show — review priority"
v8.1 loop fix — PM must update site_visit_outcome after every visit
Critical — v8.1 fixIf site_visit_outcome is never set (PM forgets), Zap 13 re-evaluates the same contact daily and fires the no-show SMS every day. Estimator must set site_visit_outcome to any non-empty value within 2 hours of every visit — even for successful visits. Add this to the Site Visit SOP as a mandatory after step.
Build · Site Visit + Estimation
Build notes
RuleNever edit territory_map sheet while Zap 6 is live. Territory changes at end of business day only — Zap 6 reads it in real time.
Demo · Site Visit
Territory routing + Good/Better/Best
3:00 – 3:45
The system knows who to send before anyone does
SAY
"Site visit books. The system looks up the ZIP code in a territory map, checks the estimator's capacity this week, assigns the right person — and enrols the client in a confirmation sequence with the estimator's direct phone number. All before anyone picked up a phone."
Phase 09 · Sales Machine
Proposal, HiTL Gate, Contract & Legal
The HiTL gate at $15K is not optional. The contract template is not optional. Every field in this section prevents a specific expensive problem that happens repeatedly without documentation.
Zaps 7,9HiTL $15KContract completenessLien waiver
Estimate follow-up sequence — the highest-stakes sequence in the system
DayActionGoal
D0 (send)Walk through estimate in a video call or phone call. NEVER email and run. "Which of the 3 options resonates with you most?"Get verbal commitment on option
D1Email: "Following up on the options I sent yesterday — did you have a chance to review?" CTA: Book a quick call to answer any questions.Re-open conversation
D3Email: Case study of a project similar to theirs. "Here's a project we did in [nearby suburb] with a similar scope." Addresses implicit objection.Build confidence
D7SMS: "Hi {{first_name}} — [rep_name] here. We have [month] slot availability opening soon. Happy to hold one for you — would that help?"Urgency
D14Email: Final follow-up. "I want to make sure you have everything you need to make a decision. Is there a specific concern I haven't addressed?"Surface the real objection
D21Move to Stalled. Rep task: "D21 stall — call {{first_name}} and ask what's holding it up."Human decision point
Zap 9 — HiTL gate for estimates >$15K
ZAP 9
estimate_amount >$15K → Slack gate + manager task + block send
T
Trigger — GHL: Field Updated (estimate_amount)
F
Filter: estimate_amount > 15000
1
Check: does estimate have 3 options populated? (estimate_option_selected field)
If only 1 option → create task "Estimate must have 3 options (GBB) before approval." Block until complete.
2
Action — Slack: Post to #manager-approvals
🔐 APPROVAL REQUIRED — {{first_name}} {{last_name}}
Estimate: ${{estimate_amount}} · Budget tag: {{budget}} · Project: {{project_type}}
Estimator: {{estimator_assigned}} · Rep: {{assigned_rep}}
GHL link: {{contact_url}}

Reply ✅ to approve · Reply ❌ to reject with note
3
Action — GHL: Create Task
Title APPROVAL — Manager review — {{first_name}} — ${{estimate_amount}}
Due +24 hours
Construction contract — what must be in it (every item protects you)
Scope of workDetailed — lists every included task. Prevents "I thought that was included" disputes. If it's not written, it wasn't agreed.
Material specificationsBrand · grade · colour · model number where applicable. Prevents substitution disputes. "Equivalent materials" is not acceptable language.
Payment scheduleDeposit (30–50%) at signing · Milestone (25–30%) at [specific stage] · Final (20–25%) at client walkthrough signoff. Each milestone described precisely.
Start date language"Estimated start: [date range], subject to permit approval and material availability." Never commit to an exact start date. Weather + permit delays are outside your control.
Change order clause"Any changes to scope must be documented in a written Change Order, signed by both parties, and paid before work proceeds on the changed scope." This sentence prevents 90% of scope creep disputes.
WarrantyWorkmanship: [X] years from completion date. Materials: manufacturer's warranty (pass-through). Both warranties void if client modifies work without written consent.
Lien rights noticeRequired in most jurisdictions. "Contractor reserves the right to file a mechanic's lien on the property if payment is not received per the payment schedule." Check local requirements.
Dispute resolutionMediation first, then binding arbitration. Specifies jurisdiction. Prevents expensive litigation on small disputes.
HOA responsibility"Client is responsible for obtaining all HOA approvals before work commences. Contractor not liable for delays or modifications required by HOA after work begins."
Build · Proposal + Contract
PandaDoc template requirements
PANDADOC SETUP
5 templates required in PandaDoc
1
Good/Better/Best Estimate Template — 3-column layout, each column an option. All GHL merge fields. Photo section for site visit photos.
2
Construction Contract Template — all sections from the table above. Payment schedule section. Signature block with date.
3
Change Order Template — original scope reference, changed scope, cost delta, signature required before work.
4
Welcome Packet Template — sent at contract signing. PM photo + intro. Project timeline (ranges). Daily SMS protocol promise.
5
Final Lien Waiver Template — issued at final payment. Filed in project record.
Demo · Proposal + Contract
The protection layer
3:45 – 4:15
The HiTL gate and the contract that prevents disputes
SAY
"Every estimate over $15K hits a hard gate. Slack message to the manager. GHL task. Nothing goes to the client without approval. One rule, one Zap, one decision — and it's never missed."
SAY
"The contract has a change order clause that says no work proceeds without a signed CO. That one sentence prevents more disputes than anything else in the document. Every item in that contract protects something specific."
Phase 10 · Sales Machine
Full Nurture System — 22 Sequences · 24-Month Architecture
The longest previous sequence was 30 days. After that, leads went cold and disappeared. This phase documents the complete nurture architecture: angle-specific sequences, 12-month passive track, 24-month past client track, loss-reason routing, and behavioral re-activation. No contact ever fully exits the system — they recycle.
22 sequences24-month client track12-month passiveLoss routing
Complete sequence map — 22 sequences with entry + exit logic
SequenceEntry triggerLengthExit condition
Comfort Nurturelead_angle = comfort + Zap 15 emails over 7dBooking created OR 7d elapsed
Home Value Nurturelead_angle = home-value + Zap 15 emails over 7dBooking created OR 7d elapsed
Lifestyle Nurturelead_angle = lifestyle + Zap 15 emails over 7dBooking created OR 7d elapsed
Protection Nurturelead_angle = protection + Zap 15 emails over 7dBooking created OR 7d elapsed
Urgent NurturePath 1+2, after angle seq D13d, 4 emails + call taskBooking OR stall
Premium EducationPath 3, after angle seq D112 days, 6 emailsBooking OR end
Long CyclePath 4, after angle seq D121 days, 7 emailsBooking OR 12-Month enrol
Standard NurturePath 5, after angle seq D130 days, 8 emailsBooking OR 12-Month enrol
Price ObjectionTag Objection:Price added (Zap 2)5 emails, value reframeBooking OR end
Social ProofGallery click (Zap 2)3 emails, projects + reviewsBooking OR end
SMS ObjectionNegative keyword reply2 SMS, re-engageReply OR end
Estimate Follow-Upestimate_amount field updatedD1/D3/D7/D14/D21→StalledContract signed OR Stalled
Site Visit ConfirmationZap 6 firesConfirm + D-1 + 2hr + estimator #Visit date passed
Pre-Construction WelcomeZap 7 (contract signed)D0 packet / D1 SMS / D2 emailproject_status = active
Contract WinZap 7 (contract signed)Deposit invoice + welcome SMSpayment.cleared
Lost Deal ExitPipeline = Closed LostD1 survey · D3 backup · D90 win-backRe-submits OR 12-Month enrol
12-Month Passive NurturePath 4/5 after Day 30 OR Recycle OR Lost after D9012 emails, first of each monthActivity → re-activate OR end
Past Client 24-Monthproject_status = complete (Zap 8)6 touchpoints over 24 monthsRe-engages for new project
Warranty Expiry60d before warranty_expiry_date2 emails: reminder + renewal offerwarranty_expiry_date passed
Review Follow-UpD10 no review after NPS ask2 touchpointsReview posted OR end
Lost: Price Trackloss_reason = PriceMonthly, 12 months, ROI-focusedRe-submits OR end
Lost: Timing Trackloss_reason = TimingSeasonal, March/September triggersRe-submits OR 24-month
12-month passive nurture — content map
MonthEmail themeContent type
Month 1"Your [project_type] — what others in [city] have done"Project showcase — matches their project type. No selling. Just inspiration.
Month 2"The 3 questions we wish clients asked us sooner"Educational — builds authority. Positions expertise without pitching.
Month 3 (March)"Spring is the most competitive booking season — here's why"Seasonal urgency. Capacity is real — use it.
Month 4"Before and after: the project that changed how a family uses their home"Story-led social proof. No features, no specs — just emotional outcome.
Month 5"How much does a [project_type] actually add to property value?"Data-backed value content. Addresses the price objection indirectly.
Month 6"6 months since your enquiry — still thinking about it?"Personal check-in from rep. Low-key re-activation offer.
Month 7–12Rotate: project showcase / educational / seasonal / social proofOne CTA per email. Never more than one.
Past client 24-month track — the long-term revenue engine
MonthTouchpointGoal
Month 3Email: "3 months in — how is everything holding up?"Check-in. Catches issues early. Builds trust.
Month 6Email: "Maintenance tips for your [project_type] — seasonally relevant"Useful content. Keeps brand visible.
Month 12Email + Rep task: "1-year anniversary — warranty status + annual review offer"Warranty reminder + soft upsell for next phase.
Month 18Email: "Many clients add [complementary project_type] at this stage"Expansion upsell. Adjacent project type based on theirs.
Month 24Email + Rep task: "2-year check-in — know anyone who might need what you got?"Referral re-ask at peak satisfaction point.
Loss reason routing — no lost lead is truly lost
loss_reason = PriceEnrol in Lost:Price Track (monthly, 12 months). Content: ROI data, financing options, project value stories. Goal: change the frame over time.
loss_reason = TimingEnrol in Lost:Timing Track. Trigger: March broadcast ("Spring is booking up") + September broadcast ("Last chance before winter"). These are natural re-entry moments.
loss_reason = CompetitorAt 6 months → send "How did your project go?" email. This is the most effective re-entry: if they had a bad experience with the competitor, they're now your hottest prospect.
loss_reason = SpouseEnrol in Standard Nurture with joint decision-maker framing. D30 rep task: "Call back with partner-friendly offer — ask to speak to both."
loss_reason = NotQualifiedEnrol in 12-Month Passive only. Do not invest sales resources. Re-score if they re-engage.
loss_reason = NoResponse3× SMS re-engagement attempt over 7 days. If no reply → 12-Month Passive. Tag Status:NoResponse.
Build · Nurture
Day 3 — add sequence messages in priority order
CriticalNever write email copy directly in GHL. Write in the Sequence Copy Library Google Sheet first (columns: sequence, email_number, day, subject, ab_variant_subject, preview_text, body, cta, ps_line). Version control matters when you A/B test.
Demo · Nurture
22 sequences — no lead ever disappears
Proof point
The 30-day problem — and the 24-month solution
SAY
"Every system has nurture sequences that run for 30 days. After that, leads go cold. They just... stop receiving anything. This system runs for 24 months. A lead who wasn't ready in month 1 gets a different email in month 6. A client who finished their project gets touched every 6 months for 2 years. There are no dead leads in this system — there are only leads waiting for the right moment."
Phase 11 · Sales Machine
Sales Playbook — 8 Objections + Competitor Handling
Every objection is a signal, not a wall. "Too expensive" means they don't yet see the value clearly enough. "Think about it" means you haven't found the real concern. This playbook gives every rep the framework to respond, not react.
8 objectionsCompetitor scriptsSequence triggers
PRICE
"Too expensive" / "Can't afford it"
Don't say
Never discount immediately. It signals your price wasn't real to begin with.
Anchor
"What's your thinking on the investment — is it the total, or the timing of the payment?" Separate price objection from cash flow objection. Different solutions.
Lifetime cost
"A [project] done right lasts 15–25 years. Over that period, Option B works out to $[X]/month. Does that framing change how it looks?"
Phase option
"We can scope a Phase 1 now and Phase 2 next year — same quality, same crew, spread over two budgets."
Sequence
Tag Objection:Price → trigger Price Objection sequence (5 emails, ROI-focused).
SPOUSE
"I need to talk to my spouse/partner"
Validate
"That makes total sense — this affects your whole home." Never push a deadline before both decision-makers have heard the pitch.
Offer joint call
"Would it help if I jumped on a quick call with both of you? Or I can put together a one-page summary they can read in 5 minutes — which would be easier?"
GHL action
Add second decision-maker contact. Note their name. Next sequence email references: "...when you and [partner_name] are both looking at this..."
QUOTES
"I'm getting 3 quotes"
Enable it
"You absolutely should. Here's what I'd ask each of them: what materials specifically? What's the warranty on workmanship vs materials? Who actually does the installation — their crew or a sub they've never used before?"
Send tool
Email them the "Questions to Ask Any Contractor" PDF. They'll come back to you as the authority — you wrote the rubric they're judging competitors against.
TIMING
"Timing's not right" / "Maybe next year"
Qualify
"Is that a budget thing or a schedule thing?" Get specific. Vague timing is never the real reason.
Lock in
"Completely fine. One option — you can lock in today's pricing with a deposit, and we start when you're ready. Materials are going up [month]."
Sequence
loss_reason = Timing → Lost:Timing Track → seasonal re-activation March + September.
BAD EXP
"Had a bad experience with a contractor before"
Listen first
"I'm sorry to hear that. What happened?" Let them vent fully. Do not rush past this. Their previous experience is your competitive advantage if you handle this right.
Show structure
For every pain point they mention, name the specific safeguard: Scope disputes → written scope + CO clause. No updates → daily SMS promise. Poor quality → photo documentation + snag walkthrough.
CHEAPER
"Found someone cheaper"
Never attack
Ask what attracted them first. Then: "Can I see their quote? I'd like to compare apples to apples — not just the number, but what's actually included."
Compare specs
Material grade. Warranty terms. Who does the install (own crew vs unknown sub). Permit included? Timeline guarantees? Price difference often disappears under comparison.
If can't close
loss_reason = Competitor. Enrol in 12-Month. At 6 months → "How did your project go?" email. Highest re-entry rate of any lost reason.
THINK
"I need to think about it"
Surface it
"Absolutely — I want you to feel confident. Can I ask: is it the price, the timeline, the process, or something I haven't addressed well enough?"
Rule
"Think about it" is never the real objection. It is a polite way of saying "you haven't resolved something for me." Ask the question.
PERMIT
"What about permits / HOA?"
Remove fear
"We do a permit and HOA check before we commit to anything. You won't be surprised mid-project."
Responsibility
HOA approval is the client's responsibility — documented in the contract. Permit filing is your responsibility — also in the contract. Both are explicit.
Build · Playbook
Roleplay training before go-live
Training methodRun all 8 objections in a 90-minute group session. Manager plays client with the objection. Rep plays rep. Debrief each one: what worked, what didn't, what exact words to change. Goal is automatic recall — not looking at a script during a live call.
Demo · Playbook
The system closes objections, not just the rep
Proof point
Objections trigger sequences — not just scripts
SAY
"Every objection has a human response AND a system response. Rep tags 'Objection:Price' on the call → the Price Objection sequence fires 5 emails over the next 2 weeks, automatically, with ROI data and value reframes. The rep handles the immediate conversation. The system handles the follow-up."
Phase 12 · Delivery Operations
Project Activation — Deposit Gate + PM Handoff
Two separate triggers — contract signed and deposit cleared — do two completely different things. Conflating them is the #1 cause of starting projects without payment. The system enforces the gate automatically.
Zap 7payment.cleared ONLYPre-construction packetDaily SMS mandatory
Zap 7 — contract signed (prepare, do NOT activate)
1
PandaDoc document.completed → GHL update
contract_signed_date · booking_sequence = 6_installation · Pipeline = Closed Won · Tag Status:Contracted
2
Enrol in Contract Win sequence
Deposit invoice email + payment link + Welcome SMS: "Your project is confirmed. PM will be in touch within 48hrs."
3
Send Pre-Construction Welcome Packet (PandaDoc)
PM photo + intro · Project timeline (ranges) · Daily SMS protocol promise · Change order explanation · Payment schedule
4
Create PM task: "Prepare for {{first_name}} — DO NOT start until payment.cleared fires"
Note on task: "Phase 3 activates via Stripe/Square webhook — not this task."
5
Fire Meta CAPI Purchase event
Value = contract_value. This closes the ad attribution loop in Meta.
Deposit cleared — Phase 3 activation (separate trigger)
1
Stripe/Square payment.cleared webhook fires
Update: project_status = active · lifecycle_stage = CLIENT · Pipeline = Deposit Pending
2
3 same-day tasks fire automatically
Order long-lead materials · File permit application · Schedule crew + kickoff call
Permit reality"File permit" means submit the application. Permit issuance takes 2–8 weeks depending on jurisdiction and project type. Architectural drawings or engineering review can add 1–2 weeks before the application can even be submitted. Never promise a start date that depends on permit issuance. Build permit lead time into all project timelines before quoting start dates to clients.
3
SMS to client
"Your project is on the books, {{first_name}}. {{pm_name}} will call you today to go over the start date."
4
Update deposit_received_date
Triggers Zap 10 (invoice automation) to log payment.
Daily site SMS — mandatory every day crew is on site
Who sends itPM — never the rep. PM uses Buildertrend daily log as source material. Zap 20 auto-drafts it from the log.
WhenBy 5pm on every day crew is on site. If not sent by 5:30pm → GHL workflow sends PM a reminder task.
What not to includeNever mention delays, problems, or issues in the daily SMS without a resolution plan in the same message. "Delay" without context = client calls immediately.
Delay message"Hi {{first_name}} — we hit a [brief description] today. We've [resolution action]. This pushes completion by [X] days. I'll keep you updated daily."
Build · Project Activation
Zap 7 build notes
Common failurePhase 3 doesn't activate after deposit: (1) Was it payment.cleared or payment.submitted? (2) Did client pay by check? Webhook won't fire — PM must manually set project_status=active. (3) Check Stripe webhook log — did the event fire at all?
Demo · Activation
The deposit gate
4:15 – 4:45
The rule that protects margin before work starts
SAY
"Contract signed — nothing happens except paperwork. The moment the deposit clears — actually clears, not just submitted — three tasks fire automatically. Same day. Materials ordered. Permit filed. PM assigned. Nobody has to remember because the system does it when the money lands."
Phase 13 · Delivery Operations
Build Scheduling — Crew · Weather · Inspections · Snags
Scheduling is not a calendar — it's a capacity management system. Overbooking destroys client relationships faster than anything else. This phase documents the master schedule, crew capacity, weather protocol, and inspection management.
Zaps 14,15Crew capacityWeather protocolInspection gates
Master schedule Google Sheet — structure
Tab 1: Weekly scheduleColumns = Week dates (Mon–Fri). Rows = Crew members + subs. Cells = Project code + phase. One glance shows all resource allocation.
Tab 2: Project trackerAll active projects: job_id · client · project_type · crew_assigned · target_start · actual_start · target_complete · actual_complete · project_phase · status.
Tab 3: Material deliveriesjob_id · material · supplier · order_date · expected_delivery · actual_delivery · PM_notified. PM receives task 2 days before each expected delivery.
Tab 4: Sub schedulesubcontractor_name · trade · project · start_date · end_date · status · insurance_expiry.
Capacity ruleIf sum of crew_hours this week > available_hours → block that week in Calendly for new site visits. GHL workflow + Calendly API. Never overbooking.
project_phase field — drives communication
pre-construction
Contract signed
mobilization
Deposit cleared
rough-work
PM updates
finishes
PM updates
punch-list
Near complete
complete
Zap 8 trigger
Zap 15 — weather check (daily 6am)
ZAP 15
Daily 6am → weather API check for all active project ZIP codes
T
Trigger — Zapier Schedule: Daily at 6:00am
1
Action — Google Sheets: Get all contacts with project_status = active
2
Action — Webhooks: GET weather API for each active project ZIP (OpenWeather API)
Check: rain probability > 60% OR temperature extremes (below 5°C / above 38°C).
F
Filter: if weather issue detected for this project's ZIP
→ Create PM task: "WEATHER ALERT — {{first_name}} project — [condition] forecast today. Communicate with client." → Slack #project-alerts: "Weather delay possible: {{project_id}}"
Inspection management
inspection_required fieldBoolean. Set by PM when permit requires inspection before next phase (e.g., footing inspection before concrete, framing before cladding).
inspection_scheduled_dateSet by PM when inspection is booked with council/building authority.
Gate ruleIf inspection_required = true AND inspection_passed_date is empty → GHL workflow blocks project_phase from advancing beyond current phase. PM must update inspection_passed_date to proceed.
Failed inspectionIf inspection fails → PM task "Inspection failed — rectify: [notes] → rebook." Track in snag_log.
Build · Scheduling
Buildertrend bridge (Month 1)
BridgeZap 19: Buildertrend job phase update → GHL project_phase update. Zap 20: Buildertrend daily log → GHL note → auto-drafts daily SMS. Zap 21: Buildertrend schedule delay → GHL schedule_variance_days update → Slack alert if >3 days.
Demo · Scheduling
The system manages capacity — you don't
Proof point
Why overbooking kills construction businesses
SAY
"At 6am every day, the system checks the weather API for every active project ZIP. If rain is forecast, a task fires to the PM before they've had coffee. The PM communicates proactively instead of the client calling angry at noon."
Phase 14 · Delivery Operations
Quality Control — Snags + Final Walkthrough SOP
Snags are the #1 driver of negative NPS and damaging online reviews. This phase documents the complete quality control system: snag logging, resolution workflow, and the final walkthrough SOP where final payment, snag sign-off, and the review ask all happen in the same moment.
Zaps 16,17Snag gate on paymentWalkthrough SOPReview ask moment
Snag management system
Logging toolGoogle Form on PM's phone. Fields: project_id, snag_description, photo_url (CompanyCam link), responsible_party (Own crew / Sub / Supplier), target_resolution_date.
Zap 16Snag form submit → Google Sheets append to snag_log → GHL increment snag_count + snags_open → Create task assigned to crew lead: "[SNAG] {{description}} — {{target_date}}" → Slack #project-alerts
Zap 17 (daily)Check snag_log: if target_date < today AND status ≠ resolved → escalation task to PM → if 2nd day overdue → Slack #project-alerts "OVERDUE SNAG — {{project_id}}"
Resolution flowCrew resolves snag → PM updates snag_log status = resolved → snags_open decrements → if snags_open = 0 → PM task "All snags resolved — ready for final walkthrough"
Payment gatefinal_payment_date cannot be set until: snags_open = 0 AND client_signoff_date is populated. GHL workflow enforces this. PM who sets final_payment_date without these = task fires to manager.
KPIavg_snags_per_project · avg_snag_resolution_days · repeat_snag_patterns (same description recurring = training issue). Review monthly in Ops dashboard.
Final walkthrough SOP — the most important 30 minutes of the project
StepWhat to doWhat to say
StartWalk every inch of the project with the client. PM leads. Client follows."I want to walk through everything together and make sure you're completely happy before we close this out."
Snag loggingIf client spots anything → log it on the spot using the snag form. Don't promise fixes verbally — document them."Let me log that right now so it's on record and we can get it resolved."
Sign-offClient digitally signs the walkthrough completion form (PandaDoc or Google Form). Sets client_signoff_date."If you're happy with everything, I'll need your signature here to confirm the project is complete."
Payment collectionCollect final payment on site (card reader or bank transfer). PM sets final_payment_date in GHL immediately."The final payment is [amount] — would you like to do that by card or bank transfer?"
Review askThis is the exact moment. Satisfaction is at its peak. Phone out. 70% conversion on the spot."One last thing — reviews mean everything to a business like ours. Would you mind leaving us a Google review right now? I can stay on the line while you do it — it takes 60 seconds."
Video capturePM takes 60-second after walkthrough video. Sets video_captured = true. Sends to editor."Do you mind if I take a quick video of the finished project? We love documenting our work."
Build · Quality + Snags
Snag system build notes
Quick buildSnag form → Google Sheets → Zap 16 is a 2-hour build. The biggest impact for the least effort. Most construction businesses track snags in texts. Moving to a documented system immediately changes how the team treats quality.
Demo · Quality
Why snags become reviews — and how to prevent it
Proof point
The review ask moment — highest conversion in the system
SAY
"The final walkthrough is the most valuable 30 minutes in the business. Payment collected. Snags signed off. And the review ask happens right there — in person — while the client is standing in the space they just invested in. Not a text the next day. Not an email a week later. Right there. 70% say yes."
Phase 15 · Delivery Operations
After-Sales Flywheel — NPS · Reviews · Referrals · VIP · Reputation
The flywheel is where the system compounds on itself. Every happy client generates a review. Every review generates trust for the next lead. Every referral enters Phase 1 as the highest-converting source. VIPs become partners. The system runs for 24 months after every completed project.
Zap 82-3× referral closeNPS D3VIP ≥3 referralsReputation system
Flywheel timeline — Zap 8
D0
final_payment_date set by PM → Zap 8 fires
project_status=complete · warranty_expiry_date=+12mo · Pipeline=Archive · lifecycle_stage=PAST_CLIENT. Enrol in Past Client 24-Month track.
D3
NPS survey SMS
"Hi {{first_name}} — your project is complete! 30 seconds of feedback: {{nps_link}}"
0–6
Detractor — PM call within 24hr
PM only — not rep. detractor_handled=false until call made. Review/referral ask blocked until detractor_handled=true.
7–8
Passive — review ask only
No referral ask. Soft touch. D90 referral ask at most.
D5
Review ask (NPS 9+)
SMS + email with direct Google/Houzz review link. D10: if no review → rep task "Call {{first_name}} — ask to post while on the phone."
D14
Referral ask with unique tracking link
referral_count increments on each referral. At 3: vip_status=true → owner personal call task.
Referral re-enters Phase 1
Source:Referral + referrer_name note. +8 intent score bonus. Rep sees referrer name before first call. 2-3× close rate vs cold.
Reputation management system
Review platformsPriority: Google (highest SEO value) → Houzz (construction-specific) → Facebook → BBB. Ask for Google first. Always.
Review monitoringBirdeye or Podium aggregates all reviews in one dashboard. Daily alert for any new review (especially 1-2 star — respond within 2hr).
Negative response SOPNever defensive. Template: "Thank you for sharing this, [name]. We take every experience seriously. [Owner name] will be in touch directly to make this right." Then take offline immediately.
GHL fieldsreview_platform (Dropdown) · review_rating (Number 1-5) · review_url (Text)
GMB cadenceEvery completed project → GMB post within 48hr. Zap 8 creates task: "Post project to GMB + tag suburb." gmb_posted must = true before task closes.
VIP + B2B referral partner program
Client VIPreferral_count ≥ 3 → vip_status=true → owner personal call → priority scheduling → annual check-in → VIP:Client tag
B2B partnersSeparate pipeline: real estate agents / interior designers / architects / landscapers / pool companies / HOA managers. Each gets a unique referral tracking URL.
Partner fieldspartner_type · partner_referrals_sent · partner_commission_rate (agreed % of contract value)
Partner nurtureMonthly newsletter: project updates + "your referral converted" updates + one featured project. Monthly task: PM/owner checks in with top 3 partners personally.
CommissionWhen contract signed from partner referral → finance task created for commission payment. Logged in finance_tracker.
Build · Flywheel
Zap 8 + reputation setup
Build orderZap 8 first (highest revenue impact — past client track). Then reputation management setup (Birdeye/Podium). Then B2B partner pipeline in Month 2.
Demo · Flywheel
Close the demo with this
5:00 – 5:30
The compound engine — every project generates the next one
SAY
"Every completed project starts a 24-month automated communication track. Review. Referral. 6-month check-in. 12-month anniversary. 18-month expansion offer. 24-month referral re-ask. Every client who goes through this system is a future revenue source. That's the compound engine."
CLOSING
"This is not a CRM setup. This is a revenue loop that runs for 24 months after every project. The system that does this work for a business that does 50 projects a year is handling 1,200 touchpoints. No human could manage that. This system does it automatically, every day."
Phase 16 · Finance Layer
Invoice Automation — Deposit · Milestone · Final · Overdue Escalation
Every invoice in the current system is manual. One forgotten deposit invoice = project starts without payment. One missed follow-up = a client who quietly doesn't pay for 45 days. This phase automates the entire invoicing lifecycle with hard gates at each stage.
Revenue-criticalZaps 10,11,123-stage billingOverdue escalation
3-stage invoice automation
ZAP 10
Contract signed → auto-create deposit invoice in PandaDoc + Stripe
T
Trigger — GHL: Field Updated (contract_signed_date)
F
Filter — deposit_invoice_date is EMPTY (v8.1 fix — prevents double invoice on field re-save)
This is the v8.0 bug fix: without this filter, if contract_signed_date is re-saved for any reason (rep edits, Zap retry), Zap 10 fires again and creates a second invoice. The filter ensures it fires exactly once.
1
Action — PandaDoc: Create Document from Deposit Invoice Template
template ASMC_Deposit_Invoice
client_name {{first_name}} {{last_name}}
email {{email}}
amount {{contract_value}} × 0.40
due_date {{contract_signed_date}} + 3 days
project_ref {{contact_id}}
2
Action — GHL: Update deposit_invoice_date = {{now}}
3
Action — GHL: Enrol in Contract Win sequence (deposit invoice email → payment link)
F
If Stripe preferred: create Stripe Payment Link instead (30% lower friction than PandaDoc invoice)
GotchaPandaDoc invoice + Stripe payment link = two separate things. PandaDoc for PDF record. Stripe for the actual payment flow. Do not send PandaDoc's built-in payment if Stripe is your primary processor — double-charges possible.
ZAP 11
PM sets milestone trigger → auto-create milestone invoice
T
Trigger — GHL: Field Updated (milestone_trigger) — PM sets this manually when milestone is reached
F
Filter: deposit_received_date is NOT empty (deposit cleared — don't invoice milestone if deposit not paid)
1
Action — PandaDoc: Create Milestone Invoice
amount {{contract_value}} × 0.35
due_date {{now}} + 7 days
milestone_desc {{milestone_trigger}} // PM writes what milestone was reached
2
Action — GHL: milestone_invoice_date = {{now}}. SMS client: "Your milestone invoice is ready — {{amount}}. Pay here: {{link}}"
ZAP 12
Daily overdue invoice check → escalation ladder
T
Trigger — Zapier Schedule: Daily 9:00am
1
Action — Google Sheets: Get all contacts where invoice_date is set AND received_date is empty
2
Code: calculate days overdue for each
const today = new Date();
const invoiceDate = new Date(inputData.invoice_date);
const daysOverdue = Math.floor((today - invoiceDate) / 86400000);
output = [{daysOverdue, contact_id: inputData.contact_id, amount: inputData.amount}];
F
Branch — D+1: SMS client friendly reminder
"Hi {{first_name}} — just a reminder, your invoice of ${{amount}} is due. Pay here: {{link}}"
F
Branch — D+3: Email + rep task "Invoice follow-up call — {{first_name}} — ${{amount}}"
F
Branch — D+7: PM phone call task (SLA: same day). Slack #finance-alerts: "OVERDUE 7d — {{first_name}} — ${{amount}}"
F
Branch — D+14: Owner escalation task. Slack direct message to owner. "INVOICE CRITICAL — 14 days — {{first_name}} — ${{amount}}"
Invoice tracking fields — full set
FieldTypeSet byDrives
deposit_invoice_dateDateZap 10Zap 12 overdue check
deposit_received_dateDateStripe payment.cleared webhookPhase 3 activation gate
milestone_invoice_dateDateZap 11Zap 12 overdue check
milestone_received_dateDateStripe webhookPhase 4 activation gate
final_invoice_dateDatePM (only after snags_open=0)Zap 12 overdue check
final_received_dateDateStripe webhook → Zap 8Flywheel activation
payment_methodDropdownPM (card/bank/check/cash)If check/cash: PM must manually set received_date — webhook won't fire
Change order system
Rule (zero exceptions)No work proceeds on changed scope until PandaDoc Change Order is signed by client AND payment link is opened. Not verbal agreement. Not "they said yes on a call." Signed CO + payment initiated.
CO triggerPM creates CO in PandaDoc → document.completed webhook → GHL: change_order_count +1, change_orders_total += amount → update Stripe invoice for new total
CO fieldschange_order_count (Number) · change_order_1_amount · change_order_2_amount · change_order_3_amount · change_orders_total
Revenue impactTrack change_orders_total separately from contract_value. In Looker Studio: actual_revenue = contract_value + change_orders_total. Many businesses lose track of CO revenue — it disappears into the job cost without being properly attributed.
Build · Invoice Automation
Week 2 — after Zaps 1,3,7 are stable
Build orderZap 10 first (biggest gap — deposit invoices are manual now). Then Zap 12 (daily check — immediate cash flow impact). Then Zap 11 (milestone — need to agree milestone definitions first).
Check/cash gapStripe webhook won't fire for check or cash payments. Add a GHL task to the PM's daily checklist: "Any check/cash payments received today? Update received_date manually." Without this, the automation chain breaks silently on non-card payments.
Demo · Invoice
Show the cash flow protection layer
Finance proof point
The invoice that nobody forgot
SAY
"Contract signed at 2pm Tuesday. By 2:03pm — the deposit invoice is already in the client's inbox. Not because someone remembered to send it. Because the system created it, populated it with the right amount, and delivered it the moment the contract was signed. And if they haven't paid by Day 3 — an SMS reminder goes automatically. Day 7 — a rep task. Day 14 — manager alert. The system never forgets."
Phase 17 · Finance Layer
Job Costing & KPIs — Margin · Reporting · 7 Looker Dashboards
Revenue is vanity. Margin is reality. This phase documents the complete job costing architecture: per-job cost breakdown, actual vs estimated margin tracking, and the 7 Looker Studio dashboards that give every role exactly the numbers they need — nothing more.
finance_tracker sheet7 dashboardsActual marginReal-time
finance_tracker Google Sheet — complete schema
ColumnTypeSourceFormula
contact_idTextGHLPrimary key — joins to all other logs
client_nameTextGHL
project_typeTextGHL
contract_valueCurrencyGHL
change_orders_totalCurrencyGHL
actual_revenueCurrencyCalculated=contract_value + change_orders_total
material_costCurrencyPM entersAll materials purchased for this job
labour_costCurrencyPM entersOwn crew hours × hourly rate
subcontractor_costCurrencyPM entersAll sub invoices for this job
permit_costCurrencyPM entersCouncil/permit fees
equipment_costCurrencyPM entersHire equipment, scaffolding, tools
disposal_costCurrencyPM entersSkip hire, waste removal
contingency_usedCurrencyPM entersAnything unplanned — track separately
total_costCurrencyCalculated=SUM(material:contingency)
gross_profitCurrencyCalculated=actual_revenue − total_cost
actual_margin_pct%Calculated=(gross_profit / actual_revenue) × 100
estimated_margin_pct%From estimatePM enters estimated margin at quote stage
margin_variance%Calculated=actual_margin_pct − estimated_margin_pct — RED if negative
7 Looker Studio dashboards — who sees what
DashboardAudienceKey metrics
1. FunnelOwner + Sales leadLead volume by source/day · CPL by campaign · Lead score distribution · Time to first response · Stage conversion rates
2. MarketingOwner + MarketingRevenue by campaign/angle · CPL → CPB → CPC (cost per contract) · Creative performance table · Organic vs paid mix
3. SalesSales lead + RepsBooking rate by rep · Site visit → estimate conversion · Estimate acceptance rate · Close rate by project type · Loss reason breakdown
4. OperationsPM + OwnerActive projects by phase · Crew utilisation % · Average project duration · Snag count by PM · Schedule variance by project
5. FinanceOwner + AccountantMonthly revenue vs target · Actual margin by project/type · Accounts receivable aging · Change order revenue · Top 10 jobs by margin
6. After-SalesOwner + PMNPS score by month · Review volume + avg rating · Referral count + source · Reactivation rate from nurture · Warranty claims open
7. Task HealthManager + OwnerOverdue tasks by owner · Task completion rate · Avg SLA compliance · Escalation count by week
KPI targets — track weekly, report monthly
≤24hr
First response time
↓ Lower is better
≥35%
Email open rate
↑ Target
≥25%
Lead-to-booking rate
↑ Target
≥40%
Estimate acceptance
↑ Target
≥30%
Gross margin
↑ Minimum
≥8.5
Average NPS score
↑ Target
≥4.5
Google review rating
↑ Target
≤7d
Snag resolution time
↓ Target
≥15%
Revenue from referrals
↑ Compound target
Build · Job Costing
Month 1 — after first 5 jobs worth of real data
RuleBuild Looker Studio LAST. It needs real data from real jobs to be meaningful. Minimum: 5 completed projects in the finance_tracker before building dashboards. Otherwise you're building reports for imaginary numbers.
Demo · Finance
The number most construction businesses don't know
Finance proof point
Actual margin vs estimated margin
SAY
"Revenue is what you invoice. Margin is what you keep. Most construction businesses know their revenue. Almost none track their actual margin per job. This system tracks six cost categories per job and compares actual margin to what was estimated. If a PM consistently comes in 8% below estimated margin, that's a pricing problem — or a scope management problem. Without this data, you'd never know."
Phase 18 · Intelligence Layer
Tasks, SOPs & Escalation Architecture
Tasks without SLAs are wishes. SOPs without tasks are wall art. This phase documents the complete task taxonomy, SLA enforcement, the SOP library that every task links to, and the daily overdue check that ensures nothing falls through.
9 task typesZap 18SLA per type8 SOPs
Complete task taxonomy — 9 types, each with SLA
Task typeTitle formatSLAOwnerSOP link
CALLCALL — {{first_name}} — Score:{{lead_score}}Score ≥16: same day · Score <16: next business dayRep→ Consultation Call SOP
PREPPREP — {{first_name}} — {{booking_date}}1 hour before bookingRep→ Call Prep SOP
ESTIMATEESTIMATE — {{first_name}} — due {{date}}48 hours from site visitEstimator→ Estimate Prep SOP
APPROVALAPPROVAL — ${{amount}} — {{first_name}}24 hours from creationManager→ Approval Protocol
PM_SETUPPM_SETUP — {{first_name}} — deposit clearedSame day as deposit.clearedPM→ Project Activation SOP
SNAGSNAG — {{project_id}} — {{description}}72 hours resolutionCrew Lead→ Snag Resolution SOP
INVOICEINVOICE — {{first_name}} — ${{amount}} — D+{{days}}Per escalation schedule (D1/D3/D7/D14)Rep → PM → Owner→ Invoice Follow-Up SOP
REVIEWREVIEW — {{first_name}} — D10 no postSame day as task creationRep→ Review Ask SOP
ESCALATIONESCALATION — {{type}} — {{first_name}}Same day, alwaysOwner/Manager→ Escalation SOP
Zap 18 — daily overdue task check
ZAP 18
Daily 9am → all overdue tasks → group by owner → Slack notification
T
Trigger — Zapier Schedule: Daily 9:00am
1
Action — GHL: Search Tasks where due_date < today AND status ≠ completed
2
Code: Group by assigned_user, count by task_type
// Group tasks by owner
const grouped = {};
inputData.tasks.forEach(t => {
if (!grouped[t.owner]) grouped[t.owner] = [];
grouped[t.owner].push(t);
});
output = Object.entries(grouped).map(([owner, tasks]) => ({owner, count: tasks.length, tasks: tasks.map(t=>`${t.title} (${t.days_overdue}d)`).join('\n')}));
3
Action — Slack: DM each owner with their overdue list
⚠️ OVERDUE TASKS — {{owner}} — {{date}}
You have {{count}} overdue tasks:
{{task_list}}

// Plus post summary to #manager-approvals if any ESCALATION type is overdue
SOP library — 8 written procedures (not just referenced)
Lead Response SOPWho does what in the first 5min / 60min / 4hr. Exact message templates per path. Escalation if no response from rep.
Consultation Call SOP20-minute structure (Phase 07). 20 discovery questions written out. Angle opener by lead_angle. Next step close script.
Site Visit SOPBefore/during/after checklist. Measurement protocol. Photo naming. Client end-of-visit script. 2hr post-visit field update rule.
Estimate Presentation SOPNever email and run. Walk-through structure. GBB presentation script. "I'll think about it" in-the-moment response.
Change Order SOPWord-for-word client conversation script. How to present the CO as protecting them, not padding the invoice. What to do if they push back.
Daily Site SMS SOPTemplate. What to include. What NOT to include. How to phrase delays. How to handle client replies. Time rule: by 5pm always.
Final Walkthrough SOPFull 30-minute structure (Phase 14). Snag logging. Sign-off. Payment collection. Review ask script. Video capture.
Complaint Handling SOPWho gets called first (PM always, not rep). What to say. What not to say. Documentation protocol. Escalation to owner at what threshold.
SOP storageSOPs live in Google Drive folder "ASMC SOPs" — linked in every relevant GHL task note. When a CALL task is created, the note includes: "See Consultation Call SOP: [Drive link]". Reps click through from the task. It's there when they need it, not buried in a handbook they read once.
Build · Tasks + SOPs
Build tasks first, write SOPs in parallel
PrioritySet up task naming convention in GHL first (enforced by creating task templates). Then build Zap 18 (overdue check). Write SOPs in Google Docs concurrently — they don't block the Zap build but must exist before training starts.
Demo · Tasks
The morning overdue digest
Ops proof point
Every morning, every overdue task surfaces automatically
SAY
"At 9am every morning, every overdue task in the system is grouped by owner and sent as a Slack DM. No manager has to chase people. No task falls through because someone forgot to check GHL. It surfaces automatically. And if an ESCALATION-type task is overdue — the owner gets a direct alert."
Phase 19 · Intelligence Layer
Operations — Staff Roles · Subcontractors · Compliance · Never-List
Every person who touches the system must know exactly what they can and cannot do. Role boundaries prevent data corruption. Subcontractor management prevents compliance failures. The Never-List prevents the most common catastrophic mistakes.
4 rolesSub managementCompliance calendarNever-List
Role matrix — what each person can and cannot do in GHL
RoleCan doCannot do
RepCreate/update contacts. Add notes. Change pipeline stage (Lead In → Appt Scheduled). Create CALL/PREP tasks. Enrol in sequences. Update lead_score.Delete contacts. Change contract fields. Set project_status. Access finance_tracker. Override HiTL gate.
EstimatorUpdate site_visit_outcome. Upload to CompanyCam. Set estimate_amount. Create ESTIMATE tasks. Update measurements_folder.Change pipeline stage beyond Proposal Sent. Override $15K approval gate. Set contract fields.
PMUpdate all project delivery fields. Set project_phase. Create snag records. Set milestone_trigger. Create daily log. Set final_payment_date (only when gates pass).Change sales fields (lead_score, path_assigned). Delete completed project records. Set final_payment_date before snags_open=0.
OwnerAll above + approve estimates >$15K + access all dashboards + override any gate with audit note.Cannot delete contact without documented reason. Every override logged.
The Never-List — actions that break the system
Never duplicate a contactAlways search phone + email before creating. Duplicates create split histories — you lose the full picture of a lead's behaviour.
Never manually set lifecycle_stagelifecycle_stage is system-controlled. Manual changes break the stagnation detection workflows that depend on stage history.
Never enrol in a sequence manually without checking current sequencesDouble-enrolment sends the same person 2 emails per day. They unsubscribe. You lose the lead permanently.
Never delete a completed project contactPast clients are in a 24-month sequence. Deletion removes them from the flywheel and from referral tracking.
Never set final_payment_date before snag sign-offTriggers Zap 8 which activates the flywheel and closes the project. If snags remain, the system treats them as resolved. Client complaint + no paper trail.
Never bypass the $15K HiTL gateEvery bypassed approval is a mispriced large job waiting to happen. If the gate fires and it's inconvenient — that's information, not a bug.
Never rename pipeline stagesZaps 1, 3, 6, 7, 8 reference exact stage names. Rename = all those Zaps fail silently.
Subcontractor management system
Sub Directory tabGoogle Sheet: sub_name · company · trade · license_number · insurance_policy_number · insurance_expiry · hourly_rate · performance_score · preferred (Boolean)
Zap 22Monthly check → any sub with insurance_expiry within 30 days → owner task "Collect updated COI — {{sub_name}} expires {{date}}" → email to sub: "Please send updated Certificate of Insurance before {{date}}"
Performance scoringPM updates subcontractor_performance_score (1–5) after every project involvement. Threshold: 3 scores below 3 = removed from preferred list. Tracked in sub_directory sheet.
Assignment ruleOnly preferred subs (performance_score ≥ 3.5 AND insurance current) get assigned to new projects. Zap 14 checks this during scheduling.
Legal + compliance requirements
SMS (TCPA)Explicit opt-in required on every form. Checkbox: "I agree to receive SMS messages from [Company]. Msg &data rates may apply. Reply STOP to opt out." Store sms_consent_timestamp — never overwrite. STOP keyword must halt all SMS sequences immediately and set sms_opt_in = false.
Email (CAN-SPAM / CASL)Every broadcast email must include a working unsubscribe link. GHL handles this automatically for sequences. Verify for any external tools sending email on your behalf. If serving clients in or from the EU/UK — add GDPR privacy acknowledgement to your form.
Data securityAPI keys in Zapier's credential vault — never in Google Sheets, Slack messages, or GHL notes. Rotate GHL API keys every 90 days. Store in company password manager (1Password / Bitwarden) — not individual inboxes.
Lien rightsCheck jurisdiction-specific requirements. Some states/provinces require lien notice language in contracts before work begins. Requirements vary significantly — legal review recommended before finalising contract template.
GHL technical noteGHL does not offer granular field-level permission locks. Role restrictions above are enforced through training, the Never-List, and manager oversight — not technical locks. This is a GHL platform limitation, not a system design choice.
QAQC coverage gap register — known future expansion items
GapCurrent statusRecommended next step
Construction ERPXero/QB bridge via Zapier only. No deep job cost sync.At $1M+ revenue: evaluate Sage 100 Contractor or Acumatica for integrated job costing + accounting.
Permit tracking stagesSingle task "file permit" — no multi-stage tracking.Add permit_stage field: not submitted / submitted / under review / approved / rejected. Add tracking workflow.
Change order enforcementStrict gate in contract; real-world verbal approval common.Add verbal_approval_log note template + 48hr CO signature SLA reminder task.
Mobile field experiencePM uses Google Forms + Sheets on mobile.Evaluate Buildertrend field app when 3+ simultaneous active projects.
Material procurement"Order materials" task only — no PO tracking.Add purchase_order tab to Master Schedule sheet. Track supplier, amount, expected delivery per line item.
Warranty service routingWarranty claim fields exist; no service ticket routing.Add warranty_claim → Slack #warranty-claims → PM task → resolution tracking workflow.
Crew payrollLabour cost entered manually by PM.At scale: evaluate Deputy or ClockShark for time tracking → automatic labour_cost calculation.
Build · Operations
Role setup + Never-List training
RuleBuild GHL user roles BEFORE giving any staff access. Set user permissions exactly as per the role matrix. Every person uses their own login — never a shared account. Shared accounts make audit trails useless.
Demo · Operations
The Never-List is a feature
Ops proof point
7 actions that don't produce errors — they produce silent corruption
SAY
"The Never-List is the most important training document in the system. These aren't errors that break things visibly. They're actions that corrupt data silently. A renamed pipeline stage and every related Zap fails. A manually changed lifecycle_stage and the stagnation detection stops working. You won't see it until two weeks of leads have gone cold without anyone noticing."
Phase 20 · Reference
All 24 Zaps — Complete Index
Every Zap documented: trigger, purpose, critical notes. Build in the order shown. Zaps 1,3,7,8 are the spine — they activate all phases. Never build Zap 5 before Zap 1 is tested. Never activate Zap 8 before the snag gate is confirmed.
24 ZapsBuild order critical5 daily schedulers3 webhook listeners
Marketing + Lead Engine (Zaps 1–5)
ZapTriggerPurposeCritical note
Zap 1Webhooks: form submitValidate → score → angle → path → sequence → log → CAPIMost important Zap. Test with real submit. Build first.
Zap 2GHL: email event (click/open) OR SMS replyIntent score update + engagement_status + stagnation detection + behavioural sequence triggersMust run on EVERY email event. Set to "any email event" not specific sequence.
Zap 3Calendly: Invitee Created (Consultation)GHL update + rep briefing note + PREP task + Slack if score ≥16 + booking_logCalendly event name must match exactly. Case-sensitive.
Zap 4GHL: tag applied (Objection:Price, Objection:Timing)Trigger relevant objection sequence + Slack rep alertTag names exact match. Space before colon = broken Zap.
Zap 5Bland AI: call completedOutcome → GHL update → if booked → Zap 3. If negative → tag + sequenceBland AI must have webhook configured in Pathway. Test extensively before live.
Sales + Estimation (Zaps 6–9)
ZapTriggerPurposeCritical note
Zap 6Calendly: Invitee Created (Site Visit)ZIP → territory lookup → estimator capacity check → assign → confirm sequenceSeparate Calendly event from Consultation. Do NOT combine events.
Zap 7PandaDoc: document.completed (contract template)GHL contract fields → CAPI Purchase event → Contract Win sequence → Welcome Packet → invoice prepPandaDoc filter: only fires for contract template, NOT estimate or CO templates.
Zap 8GHL: field updated (final_payment_date)project_status=complete → warranty date → 24-month track → NPS → GMB task → Sheets → FlywheelGates: verify snags_open=0 AND client_signoff_date populated before this Zap acts. Add filter.
Zap 9GHL: field updated (estimate_amount >15000)HiTL gate → Slack #manager-approvals → approval task → block until approvedFilter on estimate_amount > 15000. Number field comparison, not text.
Finance Automation (Zaps 10–12)
ZapTriggerPurposeCritical note
Zap 10GHL: field updated (contract_signed_date)Auto-create deposit invoice in PandaDoc/Stripe → deposit_invoice_dateFilter: fire only once (if deposit_invoice_date is currently empty)
Zap 11GHL: field updated (milestone_trigger)Create milestone invoice → send to client → milestone_invoice_dateFilter: deposit_received_date must NOT be empty. Don't invoice milestone if deposit unpaid.
Zap 12Zapier Schedule: daily 9amOverdue invoice check → D1 SMS / D3 task / D7 PM task / D14 owner escalationProfessional Zapier plan required for scheduled Zaps. Starter plan blocks this.
Scheduling + Quality (Zaps 13–17)
ZapTriggerPurposeCritical note
Zap 13Zapier Delay: site_visit_date + 45minNo-show detection → SMS client + rep task if outcome emptyDelay by Zapier — set exact delay from field value. PM must update site_visit_outcome or this fires every day.
Zap 14GHL: field updated (scheduled_start_date)Crew schedule conflict check in Master Schedule → PM task if conflictSheets lookup: count bookings for assigned crew this week. Threshold = weekly_capacity.
Zap 15Zapier Schedule: daily 6amOpenWeather API check for active project ZIPs → PM task if rain/extremeLoop through all active project ZIP codes. One API call per ZIP per day.
Zap 16Webhooks: snag form submitAppend to snag_log → GHL snag_count+1 + snags_open+1 → crew task → SlackGoogle Form webhook. Each submission = one snag. PM must log each item separately.
Zap 17Zapier Schedule: daily 8amOverdue snag check → if target_date < today AND status ≠ resolved → PM escalation taskReads snag_log sheet. Status must be exact match: "resolved" lowercase.
Intelligence + Ops (Zaps 18–24)
ZapTriggerPurposeCritical note
Zap 18Zapier Schedule: daily 9amOverdue task digest → group by owner → Slack DM per personGHL task search filter: due_date < today, status ≠ complete. Multi-step with code step.
Zap 19Buildertrend: job phase updated (webhook)Sync project_phase → GHL. If phase=Final → PM walkthrough scheduling taskBuildertrend webhook setup in account settings. Must map BT phase names to GHL dropdown values.
Zap 20Buildertrend: daily log submitted (webhook)GHL note with log content → auto-drafts PM daily SMS contentMaps BT daily log fields to GHL note template. PM reviews draft before sending.
Zap 21Buildertrend: schedule delay (webhook)Update schedule_variance_days in GHL → if >3 → Slack #project-alertsRequires Buildertrend Pro for webhooks. Not available on Basic plan.
Zap 22Zapier Schedule: monthly, day 1Sub insurance expiry check → owner task + sub email if <30 daysReads sub_directory sheet. Date comparison: expiry_date − today < 30.
Zap 23Zapier Schedule: monthly, day 1Compliance document expiry check → owner task + Slack if <60 daysSame pattern as Zap 22. Reads compliance_calendar sheet tab.
Zap 24Zapier Schedule: monthly, day 1GHL contact export → save to Google Drive with date stampGHL export API. Save to Drive folder "ASMC Backups/YYYY-MM". Monthly data insurance.
Build · All Zaps
Testing protocol for every Zap
Testing ruleEvery Zap gets 3 test runs before going live: (1) Happy path — everything works as expected. (2) Edge case — missing field, empty value. (3) Error path — what happens if a step fails. Document results. Never skip step 2.
Zapier plan checkScheduled Zaps (12,15,17,18,22,23,24) require Zapier Professional plan ($49/mo). Starter plan blocks scheduled triggers. Verify plan before building these Zaps or they'll activate but never run.
Demo · All Zaps
Show the full 24-Zap index
Reference proof point
24 automations — zero of them are "nice to have"
SAY
"24 Zaps. Every single one of them handles something that a human was doing manually — or not doing at all. The 5 daily scheduled Zaps run at 6am, 8am, and 9am every morning before your team starts work. By the time anyone sits down, the system has already checked the weather, flagged overdue snags, surfaced overdue tasks, and checked for unpaid invoices."
Phase 21 · Reference
Fields Dictionary — All 76 GHL Custom Fields
Every field has one owner — one thing that writes to it. If two things write to the same field, one will overwrite the other silently. This dictionary is the source of truth for field names, types, API keys, and what writes them.
76 fields8 categoriesSingle owner ruleBuild before Zaps
System core fields (8)
Field nameAPI keyTypeValues / Notes
lifecycle_stagelifecycle_stageDropdownTOFU / MOFU / BOFU / RECYCLE / CLIENT / PAST_CLIENT — System only. Never manual.
engagement_statusengagement_statusDropdownNEW / ENGAGED / RESPONDING / STALLED / COLD / DNC
intent_scoreintent_scoreNumberRunning total. Seeded by Zap 1. Updated by WF-3. Resets on recycle.
first_response_timefirst_response_timeNumberMinutes from contact creation to first reply. Written once.
last_activity_timestamplast_activity_timestampDateTimeUpdated by WF-3 on every behavioral event.
entered_stage_timestampentered_stage_timestampDateTimeSet every pipeline stage change. Drives time_in_stage calculation.
time_in_stagetime_in_stageNumber (hours)Calculated: NOW − entered_stage_timestamp. Drives stagnation detection.
recycle_countrecycle_countNumberIncrements each time lifecycle_stage = RECYCLE. Identifies chronic non-converters.
Marketing + Attribution fields (16)
Field nameAPI keyTypeWritten by
utm_sourceutm_sourceTextZap 1 (form URL param)
utm_mediumutm_mediumTextZap 1
utm_campaignutm_campaignTextZap 1
utm_contentutm_contentTextZap 1 (→ ad_creative_id)
utm_termutm_termTextZap 1 (Google keyword)
lead_anglelead_angleDropdownZap 1 (form field) comfort / home-value / lifestyle / protection
lead_angle_openerlead_angle_openerTextZap 1 Code step — angle-specific opening line
first_touch_sourcefirst_touch_sourceTextZap 1 — only if currently empty. Never overwrite.
last_touch_sourcelast_touch_sourceTextZap 1 — always overwrite.
ad_creative_idad_creative_idTextZap 1 (from utm_content) — matches creative tracker sheet
gclidgclidTextZap 1 (Google Click ID) — for offline conversion import
offer_claimedoffer_claimedBooleanZap 1 (if lead magnet was offered)
content_downloadedcontent_downloadedDropdownnone / value-calculator / planning-guide / questions-guide
sms_opt_insms_opt_inBooleanZap 1 (form consent field)
sms_consent_timestampsms_consent_timestampDateTimeZap 1 — TCPA compliance record
email_statusemail_statusDropdownactive / cold / bounced / DNC / unsubscribed / invalid
Sales fields — Group 3 (8 fields)
Field nameAPI keyTypeWritten by / Notes
lead_scorelead_scoreNumberZap 1 Code step. Static snapshot of score at entry. Not updated after entry — use intent_score for live behavioural score.
path_assignedpath_assignedDropdownZap 1 Code step — path1 / path2 / path3 / path4 / path5
budgetbudgetDropdownForm field / Zap 1 — Premium / Standard / Starter / Unknown
timelinetimelineDropdownForm field / Zap 1 — Urgent / Near / Long / Researching / Unknown
project_typeproject_typeDropdownForm / Zap 1 — deck / patio / fence / pergola / pool / landscaping / other
zip_codezip_codeTextForm / Zap 1. Store as TEXT not Number — leading zeros (e.g. 07001) are stripped if stored as Number. ZIP-based territory lookup breaks silently.
inbound_call_dateinbound_call_dateDateCallRail webhook Zap — on inbound call event
estimate_option_selectedestimate_option_selectedDropdownA_standard / B_premium / C_luxury — Written by estimator after GBB presentation. Tracks which option the client chose.
Finance fields — Group 4 (19 fields)
Field nameAPI keyTypeWritten by / Notes
contract_valuecontract_valueNumberZap 7 (from PandaDoc document.completed webhook). May differ from estimate_amount after negotiation.
change_order_countchange_order_countNumberCO webhook (PandaDoc document.completed filtered to CO template only)
change_order_1_amountchange_order_1_amountNumberCO webhook
change_order_2_amountchange_order_2_amountNumberCO webhook
change_order_3_amountchange_order_3_amountNumberCO webhook
change_orders_totalchange_orders_totalNumberCalculated by CO Zap (sum of CO amounts). actual_revenue = contract_value + change_orders_total
deposit_invoice_datedeposit_invoice_dateDateZap 10. Gate: Zap 10 must check this is EMPTY before creating invoice — prevents double invoice on field re-save.
deposit_received_datedeposit_received_dateDateStripe payment.cleared webhook. PM enters manually for cash/check. Gates Phase 3 activation.
milestone_invoice_datemilestone_invoice_dateDateZap 11
milestone_received_datemilestone_received_dateDateStripe webhook. Gates next phase advancement.
final_invoice_datefinal_invoice_dateDatePM — only after snags_open = 0 AND client_signoff_date is populated. System check runs on write.
final_received_datefinal_received_dateDateStripe webhook / PM manual. Gates Flywheel activation (Zap 8).
material_costmaterial_costNumberPM enters — all materials purchased for this job
labour_costlabour_costNumberPM enters — own crew hours × hourly rate
subcontractor_costsubcontractor_costNumberPM enters — all sub invoices for this job
permit_costpermit_costNumberPM enters — all council/permit fees
equipment_costequipment_costNumberPM enters — hired equipment, scaffolding, tools
disposal_costdisposal_costNumberPM enters — skip hire, waste removal
actual_margin_pctactual_margin_pctNumberCalculated in finance_tracker sheet: =(gross_profit/actual_revenue)×100. PM updates after all costs entered.
Scheduling + Quality fields — Group 5 (13 fields)
Field nameAPI keyTypeWritten by / Notes
crew_assignedcrew_assignedTextPM / Zap 14 (conflict check output)
project_phaseproject_phaseDropdownPM / Zap 19 (Buildertrend webhook sync) — pre-construction / mobilization / rough-work / finishes / punch-list / complete
inspection_requiredinspection_requiredBooleanPM — set when permit requires inspection before next phase. Gate: if true AND inspection_passed_date is empty → project_phase cannot advance.
inspection_scheduled_dateinspection_scheduled_dateDatePM
inspection_passed_dateinspection_passed_dateDatePM — gate field. Required to advance phase when inspection_required = true.
video_capturedvideo_capturedBooleanPM — set true after final walkthrough video taken. Feeds YouTube + GMB posting workflow.
target_start_weektarget_start_weekText (YYYY-WW)PM — set at deposit clearance. Drives capacity calendar and Zap 14 scheduling conflict check.
snag_countsnag_countNumberZap 16 — increments on each snag form submit. Total snags ever logged on this project.
snags_opensnags_openNumberZap 16 (increment) / Zap 17 (decrement on resolution). Zap 8 filter must use "Number is less than 1" — not text match "0".
final_walkthrough_datefinal_walkthrough_dateDatePM
client_signoff_dateclient_signoff_dateDatePM — set at final walkthrough sign-off. Gate for final_invoice_date and Zap 8.
gmb_postedgmb_postedBooleanPM — must = true before Zap 8 task closes. Drives organic SEO via project completion posts.
review_platformreview_platformDropdownRep after review posted — Google / Houzz / Facebook / BBB
review_ratingreview_ratingNumber 1–5Rep — drives After-Sales Looker dashboard avg rating metric
Survey + Warranty fields — Group 6 (7 fields)
Field nameAPI keyTypeWritten by / Notes
warranty_expiry_datewarranty_expiry_dateDateZap 8 — set to final_payment_date + 12 months automatically
warranty_claim_datewarranty_claim_dateDateClient warranty claim form (linked in post-completion email)
warranty_claim_descriptionwarranty_claim_descriptionTextClient warranty claim form
warranty_resolution_datewarranty_resolution_dateDatePM — after resolution. Drives avg warranty resolution time KPI.
mid_project_sat_scoremid_project_sat_scoreNumberMid-project survey form (optional). Score < 3 → PM escalation task same day.
consult_sat_scoreconsult_sat_scoreNumberPost-consultation survey form. Score < 3 → manager task immediately. Catches rep performance issues.
loss_followup_dateloss_followup_dateDateSystem (D90 trigger after Closed Lost). Next scheduled human touchpoint after automated sequences end.
VIP + Referral fields — Group 7 (5 fields)
Field nameAPI keyTypeWritten by / Notes
referral_countreferral_countNumberZap 8 / referral form submission. Drives VIP threshold.
referral_sourcereferral_sourceTextZap 8 / referral form (name of referrer). Rep sees this before first call with referred lead.
vip_statusvip_statusBooleanZap 8 — set true when referral_count ≥ 3 → triggers owner personal phone call task.
partner_typepartner_typeDropdownrealtor / designer / architect / landscaper / pool / HOA — B2B partner contacts only
partner_commission_ratepartner_commission_rateNumberAgreed % of contract value — B2B partner contacts only. Drives commission finance task on contract signed.
Build · Fields
Field creation protocol
ProtocolCreate fields in GHL in the exact order listed: System → Marketing → Sales → Finance → Scheduling → Quality → Survey → Warranty. Export field list to Google Sheet after creation — include the auto-generated API key next to each field name. Zapier uses API keys, not display names. A typo in the API key = silent Zap failure.
Demo · Fields
Show the complete field map as architecture proof
Architecture proof point
76 fields — each one does one thing
SAY
"Every field in this system has one thing that writes to it. Not two. Not three. One. That's how data stays clean over 2 years of operation. The alternative — where reps and automations write to the same fields — creates data drift that breaks reporting silently over time."
Phase 22 · Reference
Troubleshooting — Symptoms → Root Cause → Fix
Every failure mode in this system has a tell. This page documents the most common symptoms, what actually causes them (rarely what it looks like), and the exact fix. Start here before opening a support ticket.
9 failure modesSilent failuresRoot causes

🔴 Zap 1 fires but contact not in GHL

Cause: Zapier sample data used during build (not a real form submit). Sample data often has different field names than real submissions.
Fix: Delete test contacts. Submit the actual form. Use real test data. Check Zapier task history — look at the exact payload the webhook received. Does field name match exactly what GHL expects?

🔴 Sequences not sending

Cause 1: Sequence shell was created but never had messages added.
Cause 2: Contact's email_status = bounced or unsubscribed — GHL silently skips them.
Cause 3: Sending domain not verified. Check GHL → Settings → Email Services → domain authentication status.
Fix: (1) Check sequence has at least one published message. (2) Check contact's email field. (3) Verify SPF/DKIM in GHL settings.

🟠 Lead score correct but wrong path assigned

Cause: Budget/timeline field values in the form don't exactly match the code step's string comparison. "premium" ≠ "Premium".
Fix: In Zap 1 Code step — print console.log(inputData.budget, inputData.timeline) and check Zapier logs. Match capitalization exactly.

🟠 Zap 8 fires but client NOT moved to Archive

Cause: Filter check — snags_open was 0 and client_signoff_date was populated, but the field comparison in the Zap used text "0" not number 0.
Fix: In Zapier filter, use "Number is less than 1" not "Text exactly matches 0".

🟠 Deposit invoice sends but client says they didn't receive it

Cause 1: Email went to spam. Check client's spam folder first.
Cause 2: PandaDoc sent to wrong email (GHL had old email).
Cause 3: DKIM not set up — emails flagged as unauthenticated.
Fix: Verify DKIM in GHL. Check PandaDoc sent document list. Resend manually from PandaDoc dashboard.

🟠 Territory routing assigns wrong estimator

Cause: ZIP code in form doesn't match territory_map exactly. Leading zeros stripped (e.g., ZIP 07001 becomes 7001).
Fix: In territory_map sheet, set ZIP column as TEXT format (not Number). In Zap 6, ensure ZIP field is treated as text in the Sheets lookup.

🟠 Intent score not updating on email opens

Cause: Apple Mail Privacy Protection. Apple prefetches emails, triggering opens without real engagement. This inflates open counts and intent scores.
Fix: Don't score email opens from Apple Mail devices (flag: user_agent contains "Apple"). Score email clicks only — they're unambiguous intent signals.

🟠 Daily scheduled Zaps (12,15,18) not running

Cause: Zapier plan is Starter or Professional — scheduled Zaps require Professional or higher.
Secondary cause: Zapier account was over task quota — scheduled Zaps paused.
Fix: Check Zapier plan. Check task usage dashboard. If at quota, upgrade or audit which Zaps are generating excessive tasks.

🟠 Duplicate contacts appearing weekly

Cause: GHL Find Contact step in Zap 1 searches by email but contact exists with different email + same phone. Two records created.
Fix: Add a second Find Contact step in Zap 1 — search by phone BEFORE searching by email. If phone match found, use that record ID for all subsequent updates. Never let two records have the same phone number.

🟢 Looker Studio showing no data

Cause: Looker was built before enough data existed in Sheets. Data connection is correct but tables have <5 rows.
Not a bug — build Looker after 30 days of live system data. The dashboards need historical data to show trends. Before then, look at raw Sheets data directly.

Zap error diagnostic checklist
Step 1Open Zapier → Zap History. Find the failed run. Click into it and read the exact error message — don't guess.
Step 2Check the input data. Did the field arrive with the expected value? Empty fields are the #1 cause of Zap errors.
Step 3Check if the GHL field still exists. Renamed fields break the mapping — field shows as "unmapped" in Zapier action.
Step 4Check Zapier account health: task quota, plan level, connected app auth tokens (GHL tokens expire — reconnect periodically).
Step 5Re-test with a clean data submission. Never fix a Zap based on the broken run alone — the test confirms the fix works.
Build · Debug
Error monitoring setup
Set up nowEnable Zapier error notifications: Settings → Notifications → Email me when Zaps error. This catches silent failures immediately instead of discovering them when a client complains.
Demo · Debug
Know the failure modes before they happen
Trust-building moment
Showing the failure modes is a feature, not a weakness
SAY
"The fact that we have a troubleshooting guide for 9 specific failure modes isn't a weakness — it's experience. We know exactly where these systems fail because we've built and run them. Each item in this guide is something we caught, diagnosed, and fixed. You benefit from that history before you even start."
Phase 23 · Reference
Tech Reference — APIs · Upgrade Triggers · Bridge Architecture
The current stack handles a business doing 0–50 projects per year. This page documents the exact signals that mean it's time to upgrade, the upgrade path for each tool, and the API endpoints for the custom integrations that can't be done natively.
API endpointsUpgrade triggersTool bridgesScale checklist
Upgrade triggers — when to move off each tool
Google Sheets
Airtable / Supabase
When: Sheets takes >5 seconds to load (data volume) OR multiple people editing simultaneously = conflicts. Supabase adds SQL queries and real-time sync. Airtable adds better views without code.
PandaDoc (Estimate)
Buildxact
When: 5+ estimates per week from physical site visits. Buildxact has material takeoff, supplier integration, and construction-specific templates that PandaDoc cannot match.
Manual PM daily log
Buildertrend
When: 3+ simultaneous active projects. Buildertrend's field app, photo organisation, and schedule management saves 2+ hours per PM per day. ROI is clear at this scale.
Looker Studio (free)
Looker / Metabase
When: Looker Studio is too slow or you need more than 5 data sources combined, or you need alerting (Looker Studio has no alerts).
GHL Starter
GHL Pro / Agency
When: >1 sales rep needing own pipeline view OR multiple locations OR need advanced reporting features.
Zapier Professional
Make (Integromat)
When: Zapier task costs exceed $200/mo. Make runs the same workflows at ~30% of the cost at high volume. Migration requires rebuilding all Zaps.
Key API endpoints reference
ToolEndpointUsed for
GHL Contact UpdatePUT /contacts/{id}All field updates from Zapier. Requires Bearer token from GHL settings → Integrations → API Keys.
GHL Task CreatePOST /contacts/{id}/tasksAll task creation. Body: title, dueDate, assignedTo, description.
Meta CAPIPOST graph.facebook.com/v18.0/{pixel_id}/eventsLead event (Zap 1) and Purchase event (Zap 7). Requires access_token from Meta Business Manager.
OpenWeatherGET api.openweathermap.org/data/2.5/weather?zip={zip}&appid={key}Zap 15 weather check. Free tier = 60 calls/min. Plenty for daily checks.
ZeroBounceGET api.zerobounce.net/v2/validate?email={email}&api_key={key}Zap 1 step 0.5. Returns: valid / invalid / catch-all / unknown / disposable.
CalendlyGET api.calendly.com/event_inviteesIf polling needed (webhook not available in specific plans).
Bland AIPOST api.bland.ai/v1/callsZap 5. Trigger outbound AI call. Body: phone_number, pathway_id, request_data (angle, name, opener).
The tool bridge architecture — what talks to what
GHL ↔ XeroZapier bridge: contract_signed → create Xero invoice. payment.cleared → mark Xero invoice paid. Monthly reconciliation task fires automatically.
GHL ↔ BuildertrendZaps 19–21. Phase updates flow both ways. Buildertrend is the field source of truth. GHL is the client communication source of truth. Never duplicate data — each owns its domain.
GHL ↔ CompanyCamZap: project milestone → trigger CompanyCam project creation with same job_id. CompanyCam → GHL note on photo milestone with gallery URL.
GHL ↔ CallRailInbound call → CallRail assigns tracking number → webhook → Zap → find GHL contact → note call + recording URL. Missed call → SMS via GHL workflow.
GHL ↔ Birdeye/PodiumReview posted → Birdeye webhook → Zap → GHL update review_platform + review_rating + review_url → if 1-2 star → manager alert task.
Scale checklist — when the system needs restructuring
50+ leads/monthAdd a second rep. Split pipeline by rep. Add rep_assigned field to all Zap 1 routing logic. Add rep-specific KPI tab to Sales dashboard.
20+ active projectsAdd a second PM. Reassign Zap routing by project geography. Split PM dashboard by PM name.
3+ estimatorsMove site visit scheduling from Calendly to a dedicated scheduling tool (Servicetitan / Acuity with resource booking). Capacity management becomes critical at this scale.
$2M+ annual revenueDedicated accountant or bookkeeper. Move from Xero → dedicated construction accounting (Sage 100 Contractor). Separate entity for each project (liability protection).
2 locationsSeparate GHL sub-accounts per location. Shared reporting in Looker Studio using combined data source. Territory routing becomes location-aware.
Build · Tech Reference
API key management
Security ruleAPI keys stored in Zapier's built-in credential storage — never in Zap field values, never in Sheets cells, never in Slack messages. Rotate GHL API keys every 90 days. Every key stored in a company password manager (1Password / Bitwarden), not individual inboxes.
Demo · Tech Reference
This system scales with you
Long-term confidence
Built to grow — not to be replaced
SAY
"This system is built for 0–50 projects per year. But it's also documented for what happens after that. At $2M revenue, here's what changes. At 3 estimators, here's the upgrade. The system doesn't have a ceiling — it has a documented growth path. You're not building something you'll have to throw away in 18 months."
Phase 24 · Reference
Automation Alternatives — Make · n8n · ActivePieces
Zapier Professional works and has the best GHL native support. But at high task volume it becomes the most expensive option by far. This page documents full equivalents for all 24 Zaps on Make (Integromat), n8n, and ActivePieces. Choose one platform and build the entire system on it — never mix platforms across the same workflow.
4 platformsFull Zap equivalentsCost comparisonn8n self-hosted free
Platform comparison — choose once, commit fully
PlatformBest forCost at 10K tasks/moGHL supportKey difference
Zapier ProfessionalNon-technical teams, fastest setup, maximum GHL native support~$49/mo✅ Full nativeMost expensive at volume. Auto-manages GHL OAuth. Best for fast launch.
Make (Integromat)Cost-conscious teams comfortable with visual flow builders~$9/mo✅ Full native~80% lower cost than Zapier at scale. Visual canvas. Routers = Zapier filters. Iterators loop arrays.
n8nDeveloper on team, self-hosting, maximum control~$20/mo cloud or free self-hosted⚠ HTTP nodes onlyMost powerful. Self-hosted on $5/mo VPS = unlimited. Requires Bearer token management.
ActivePiecesOpen-source priority, self-hostable, moderate technical comfortFree self-hosted⚠ HTTP actions onlyCleaner UI than n8n. Growing integrations library. Simpler than n8n for non-developers.
Decision guideNon-technical team wanting fastest launch → Zapier. Cost-conscious + visual builder comfort → Make. Developer available + want full control → n8n. Open-source priority + self-hosted → ActivePieces. Running >5,000 tasks/month on Zapier → migrate to Make (30% of cost for identical workflows).
Make (Integromat) — full equivalent configuration
MAKE · Scenario 1
Zap 1 equivalent — Lead Capture
T
Webhooks › Custom Webhook
1
HTTP › Make a Request → ZeroBounce validate
GET api.zerobounce.net/v2/validate?email={{email}}&api_key={{key}}
R
Router: Branch A (valid) / Branch B (invalid/disposable)
Branch B: GHL › Update Contact (email_status=invalid) → STOP. Branch A continues.
2
GHL › Search Contact by Phone (dedup — phone first)
R
Router: found / not found → if not found → GHL › Search Contact by Email
3
GHL › Create/Update Contact
4
Tools › Set Variable — score calculation (Make supports ternary expressions natively)
// In Make "Set Multiple Variables" module — use field mapping expressions:
score = ({{budget}} = "Premium" ? 10 : {{budget}} = "Standard" ? 5 : {{budget}} = "Starter" ? 2 : 0)
      + ({{timeline}} = "Urgent" ? 15 : {{timeline}} = "Near" ? 10 : {{timeline}} = "Long" ? 5 : {{timeline}} = "Researching" ? 2 : 0)
path = (score >= 21 ? "path1" : score >= 16 ? "path2" : score >= 11 ? "path3" : score >= 6 ? "path4" : "path5")
5
GHL › Update Contact (score fields, path, opener, first_touch)
6
GHL › Add Tags
7
GHL › Enrol in Sequence (×2 — angle first, path second)
8
Google Sheets › Add Row (lead_log)
9
HTTP › Make a Request → Meta CAPI Lead event (POST)
MAKE · Scenario 12
Zap 12 equivalent — Daily Overdue Invoice Escalation
T
Make › Scheduling — Daily at 9:00am
1
Google Sheets › Search Rows (invoice_date set AND received_date empty)
2
Iterator — loop through each overdue row. Tools › Set Variable: daysOverdue = TODAY − invoice_date
R
Router: branch on daysOverdue value
D+1: GHL SMS reminder · D+3: GHL email + rep task · D+7: PM task + Slack #finance-alerts · D+14: Owner task + Slack DM to owner
MAKE · Scenario 15
Zap 15 equivalent — Daily Weather Check
T
Make › Scheduling — Daily at 6:00am
1
Google Sheets › Search Rows (project_status = active)
2
Iterator → HTTP request per project ZIP → OpenWeather API
R
Router: rain_probability > 60 OR temp < 5 OR temp > 38 → GHL task + Slack #project-alerts
n8n — full equivalent configuration
N8N · GHL HTTP Pattern
Reusable GHL API node configuration for all 24 workflows
Pattern
HTTP Request node — store credential in n8n Credentials, never hardcode in node
{
  "node": "HTTP Request",
  "method": "PUT",
  "url": "https://rest.gohighlevel.com/v1/contacts/{{contactId}}",
  "headers": {
    "Authorization": "Bearer {{$credentials.ghlApiKey}}",
    "Content-Type": "application/json"
  },
  "body": { "customField": { "field_api_key": "value" } }
}
CriticalStore GHL API key in n8n Credentials (Settings → Credentials → Header Auth). Rotate every 90 days. Never hardcode in workflow nodes — visible in exported JSON.
N8N · Workflow 1
Zap 1 equivalent — Lead Capture (full node sequence)
T
Webhook node (receive form POST)
1
HTTP Request node → ZeroBounce validation
IF
IF node: email valid? False branch → HTTP Request (GHL update email_status=invalid) → Stop
2
HTTP Request → GHL find contact by phone (dedup, phone first)
IF
IF node: contact found? False → HTTP Request (GHL find by email) → IF found → merge, else create
3
HTTP Request → GHL create/update contact
4
Function node — score + path calculation (same JS as Zapier Code step, copy directly)
// n8n: access input via items[0].json.fieldName
const inputData = items[0].json;
let score = 0;
const budgetMap = { Premium: 10, Standard: 5, Starter: 2, Unknown: 0 };
const timelineMap = { Urgent: 15, Near: 10, Long: 5, Researching: 2, Unknown: 0 };
score += budgetMap[inputData.budget] || 0;
score += timelineMap[inputData.timeline] || 0;
if (inputData.utm_source === 'referral') score += 8;
const path = score>=21?'path1':score>=16?'path2':score>=11?'path3':score>=6?'path4':'path5';
return [{ json: { ...inputData, score, path, opener: openers[inputData.lead_angle]||'' } }];
5
HTTP Request → GHL update score fields · HTTP Request → GHL apply tags · HTTP Request × 2 → GHL enrol sequences · Google Sheets node → lead_log · HTTP Request → Meta CAPI
Self-host
Docker setup for production n8n (PostgreSQL backend recommended)
docker run -it --rm --name n8n -p 5678:5678 \
  -v ~/.n8n:/home/node/.n8n \
  n8nio/n8n
# Production: use docker-compose with PostgreSQL + Nginx + SSL
# SQLite default not suitable for production use
ActivePieces — configuration notes
What it isOpen-source Zapier alternative. Self-hostable (free unlimited) or cloud ($100/mo). Cleaner UI than n8n for non-developers. GHL not natively integrated — use HTTP action for all GHL calls.
GHL HTTP pattern
Action: HTTP (POST/PUT/GET)
URL: https://rest.gohighlevel.com/v1/contacts/{{contactId}}
Headers: Authorization: Bearer [stored credential] | Content-Type: application/json
Body: { "customField": { "field_api_key": "value" } }
Scheduled triggersUse "Schedule" trigger piece. Supports cron expressions. Equivalent to Zapier Schedule trigger. Zaps 12, 15, 17, 18, 22, 23, 24 all use scheduled triggers.
Webhook triggersUse "Webhook" trigger piece — generates unique URL. Equivalent to Zapier Catch Hook. Used by Zaps 1, 7, 8, 16.
Code stepJavaScript (Node.js runtime). Same scoring code from Zap 1 runs without modification.
When to choose over n8nIf team wants simpler visual interface than n8n and is comfortable with HTTP actions for GHL API calls. Lower learning curve, slightly fewer capabilities.
Self-host costFree on any $10–20/month VPS. Docker-based same pattern as n8n. Cloud plan starts at $100/month.
Migration path — if you start on Zapier and want to switch
Logic portabilityThe workflow logic is identical across all platforms. Only the UI and connection method differs. Zap recipes translate directly to Scenarios (Make) or Workflows (n8n) with the same trigger → action structure.
Zapier → MakeRebuild Scenarios one at a time. Start with non-critical Zaps (18, 22, 23) to learn Make before migrating the revenue spine (1, 3, 7, 8). Run both in parallel for 1 week before deactivating Zapier.
Zapier → n8nLonger migration — requires HTTP node setup for all GHL calls. Allocate 1–2 weeks for developer. High payoff: unlimited tasks at zero cost on self-hosted.
When to migrateZapier costs >$200/month (approximately 8,000+ tasks) → Make costs ~$29/month for equivalent volume. ROI on migration time < 3 months.
What not to doNever run half the system on Zapier and half on Make simultaneously. Debugging becomes impossible and webhook conflicts arise. Commit to one platform fully.
Build · Automation Alternatives
Platform selection — before you write a single Zap
RuleAnswer these questions before choosing: (1) Do you have a developer on the team? If yes → n8n. (2) Are you expecting >5,000 tasks/month? If yes → Make or n8n. (3) Do you need the fastest possible launch with minimal setup? → Zapier. (4) Is cost the primary constraint? → n8n self-hosted. Choose once. Build everything on that platform. Never split.
MAKE SETUP
Make account setup — before first Scenario
1
Create Make account → Connect GHL module → Set up OAuth (auto-managed by Make)
2
Connect Google Sheets, Slack, Calendly, Stripe, PandaDoc, ZeroBounce modules
3
Build Scenario 1 first. Test with real form submit. Verify GHL contact created with all fields before building anything else.
4
Add Error Handler modules to Scenarios 1, 7, 8, 10, 12 — these touch money or client activation. Errors must surface immediately, not silently.
Demo · Automation Alternatives
The system is platform-agnostic
Technical credibility
Same logic, four platforms, radically different cost curves
SAY
"The workflows we built are not locked into any one platform. The scoring logic, the routing rules, the escalation sequences — all of it is documented and platform-agnostic. You can run this on Zapier today and migrate to Make at 30% of the cost next year without rebuilding the logic. The system is yours. The platform is just the plumbing."
Related Grimoires
003
GHL 101: The Button-to-Button Manual
GoHighLevel step-by-step — every procedure as exact click-paths. 18 modules.
005
Zapier 101: Business Automation Basics
Triggers, Zaps, paths, webhooks, error handling, and when to upgrade.
006
Automations 101: The Absolute Guide
25 production-grade patterns — idempotency, retries, DLQs, runbooks.