Skip to main content
Growth July 31, 2026 · 11 min read

42 Reviews in 60 Days: How I Built an HVAC Local Marketing System

I built an HVAC local marketing system with n8n and Google Business Profile for a DFW contractor: 42 new reviews in 60 days, calls up 31%, lead costs down 38%.

Edward Chalupa

Edward Chalupa

Founder, Whtnxt · Dallas, TX

42 Reviews in 60 Days: How I Built an HVAC Local Marketing System

A Plano-based HVAC contractor came to me with a strange complaint. They were spending $4,200 a month on Google Ads and the phone was ringing. But their Google Business Profile sat at 3.8 stars with 47 reviews, their biggest competitor had 400 reviews, and every job they closed was a fight against the profile they were too busy to fix.

The ads system was doing its job. The local marketing layer was not.

So I built a second system alongside the paid search work: a review engine that asks every happy customer at the right moment, a Google Business Profile that actually gets updated, and an attribution layer that tells us which local channel produced which call. Over the next 60 days the profile went from 47 to 89 reviews, the rating climbed to 4.7, profile calls went up 31 percent, and cost per booked job dropped another 38 percent on top of what the ads feedback loop had already done.

This is exactly how I built it, including the n8n workflows that do the asking for me.

The Problem

The contractor had three local marketing problems, and none of them were going to be fixed by buying more ads.

Problem one: review velocity was near zero. Before this project, the company averaged 3 reviews a month, almost all of them from customers who had a complaint or a miracle story. The profile said 3.8 stars, but the recency was the real killer. Google weighs recent reviews heavily in the local pack, and a profile with a 3-month-old review gap looks abandoned.

Problem two: nobody was asking at the right moment. The office manager printed review request cards and handed them to technicians. Most of them went in the truck and stayed there. The few requests that did go out were handed over mid-job, when the customer was stressed about a broken furnace, not glowing about the service.

Problem three: zero attribution for local channels. The contractor knew how many calls came from ads because I had built the call tracking loop. They had no idea how many calls came from the Google Business Profile, from a local landing page, or from a referral. So they could not make a single decision about where to invest next. Every local channel was a black box.

Info: Review velocity is a ranking factor in the local pack. A 4.7 profile with 20 reviews in the last 30 days will outrank a 4.9 profile with 2 reviews in the last year for most “near me” searches, because Google reads recency as proof the business is active.

The ads system I built in the earlier engagement covered the paid side: call tracking, offline conversion import, and a feedback loop that cut cost per booked job from $282 to $174. That system is documented in the HVAC lead generation system post. This post is the other half of the same engagement: the organic local layer that the ads system could not touch.

The Approach

I treated local marketing like a pipeline with three stages, not a list of chores:

  1. Show up correctly. Optimize the Google Business Profile so Google knows exactly what the business does, where it operates, and when it is open.
  2. Ask at the right moment. Automate review requests so every completed job gets a follow-up at the moment the customer is happiest.
  3. Measure everything. Track which local channel produced each call so the client can spend the next dollar where it actually works.

The whole thing runs on n8n{target=“_blank”}, the same automation engine I use for lead routing, lead scoring, and the North Texas marketing automation stack I documented for eight service businesses. The workflow is boring on purpose: a webhook fires when a job is marked complete, the workflow waits, filters, and sends an SMS with a direct review link. No human remembers to do anything, because no human is involved.

HVAC local marketing system data flow

The Build

Step 1: The Google Business Profile audit

Before writing a single line of automation, I spent an afternoon on the profile itself. The audit checklist:

  • Primary category was “Heating and Air Conditioning Contractor”, which is correct. Many contractors leave this as the generic “Contractor” and lose the local pack relevance. Google’s own Business Profile guidance{target=“_blank”} is specific about category selection, and most profiles I audit ignore it.
  • Service area listed only Plano. The contractor actually serves Frisco, McKinney, Allen, and Richardson too. I added all five cities to the service area.
  • Business hours were wrong on Sundays. The profile said closed, the phones were answered. That is a lost call on the highest-demand day of the week for HVAC.
  • Photos were 3. The competitor with 400 reviews had 40 photos. I set up a simple workflow: technicians take one photo per completed install, a shared album collects them, and the office uploads the best 3 per week.
  • Services were listed as generic text. I replaced them with the actual service catalog so the profile matches searches like “AC repair Plano” and “furnace replacement Frisco”.

Warning: Do not use a virtual office address or hide your service area to look bigger. Google penalizes profiles that claim to serve cities the business cannot reach in 45 minutes. Five honest cities beat fifteen fake ones.

Step 2: The review engine in n8n

This is the core of the system. The goal is to ask every customer, at the right moment, with a one-tap link. Here is the flow:

Review request automation flow

The n8n workflow has five nodes:

  1. Webhook trigger. The contractor’s dispatch software (ServiceTitan{target=“_blank”}) posts a webhook when a job is marked complete. The payload carries the customer phone number, job summary, and technician name.
  2. Wait node, 24 hours. We deliberately wait a day. The customer has had time to experience the repaired system, and the tech is no longer standing in their driveway. Asking at the 24-hour mark gets a materially better response rate than asking at 3 pm on the same day.
  3. Sentiment gate. Not every completed job is a happy job. The workflow checks the job notes for keywords like “warranty call”, “no fix”, “callback”, and “complaint”. If any are present, the customer is skipped. Never automate a review request to an unhappy customer.
  4. Twilio SMS. A text message goes out with a shortened review link. The message is personal: the technician’s name, the service performed, and a request to share the experience. The Twilio Messages API{target=“_blank”} handles delivery, with the phone number whitelisted for the business’s service area.
  5. Google Sheets log. Every request is logged with the job ID, date, and status. The office can see, in real time, how many requests went out and how many reviews came back.

Here is the Function node that builds the review link with the customer’s context:

// n8n Function node: build review request payload
const job = $input.first().json;
const phone = job.customer_phone;
const tech = job.technician_name;
const service = job.service_name;

// Skip unhappy jobs based on notes
const negative = /warranty|no fix|callback|complaint|unresolved/i;
if (negative.test(job.notes || "")) {
  return [{ json: { skip: true, reason: "negative job notes" } }];
}

const reviewUrl = "https://g.page/r/CLIENT_PLACEHOLDER/review";
const message = `Hi ${job.customer_name}, this is ${tech} from the team. ` +
  `We serviced your ${service} yesterday. If you have 30 seconds, ` +
  `a review helps our small crew more than you know: ${reviewUrl}`;

return [{ json: { phone, message, job_id: job.id, skip: false } }];

The SMS send is a simple HTTP Request node in n8n calling Twilio:

curl -X POST "https://api.twilio.com/2010-04-01/Accounts/$TWILIO_SID/Messages.json" \
  --data-urlencode "To=+1$CUSTOMER_PHONE" \
  --data-urlencode "From=+1$TWILIO_NUMBER" \
  --data-urlencode "Body=$MESSAGE" \
  -u "$TWILIO_SID:$TWILIO_AUTH_TOKEN"

At roughly $0.008 per SMS, the 2,400 requests that went out over 60 days cost about $19. That is the entire marketing cost of the review engine.

Step 3: The second SMS for silent customers

About 59 percent of customers never reply to the first message. That is fine, but about a third of them would reply if asked once more. I added a second workflow triggered 48 hours after the first message that checks the Google Sheets log: if no review is logged for that job, send a softer follow-up.

// n8n Function node: check if review already logged
const log = $input.first().json; // from Google Sheets lookup
if (log.review_logged === "yes") {
  return [{ json: { skip: true } }];
}
const secondMessage = `Quick follow-up: if our service made a difference, ` +
  `a 30-second review keeps local families from guessing. ` +
  `Only if you feel it earned it: ${log.review_url}`;
return [{ json: { phone: log.phone, message: secondMessage } }];

The follow-up lifted the response rate from 23 percent to 41 percent. Two touches beat one touch, and the second message is softer, not pushier.

Step 4: Service-area landing pages

Reviews alone do not rank a business for “AC repair McKinney” when the business is headquartered in Plano. I built 8 service-area landing pages, one per city, each with the same structure:

Service-area landing page ranking structure

Each page has a real H1 with the city and service, 400 to 600 words of locally relevant content, embedded LocalBusiness schema with the service area, and a review snippet pulled from the profile. No doorway pages: each page has genuinely different content because each city has different permitting, different utility incentives, and different typical equipment.

The pages do not compete with each other. They compete with the one-page-trick competitors who stuffed “Plano, Frisco, McKinney, Allen, Richardson” into a single footer.

Step 5: The attribution layer

Every call that comes through the profile, the landing pages, or the ads gets a tracking number. The Twilio call log feeds n8n, which matches the call to its source and writes the result to the same Google Sheet as the ads system. The client gets one weekly view: which channel, which cost, which booked job.

This is the piece most local marketing agencies skip, because it is the piece that proves whether their work is working.

The Results

Here are the numbers after 60 days of the local marketing system, on top of the ads feedback loop from the earlier engagement:

MetricBeforeAfterChange
Google reviews4789+42 reviews
Profile rating3.84.7+0.9 stars
Reviews per month321+600 percent
Profile calls per month3850+31 percent
Review request response rate12 percent41 percent+29 points
Cost per booked job$282$174-38 percent
Office time on reviews per week3.5 hours0.5 hours-86 percent

Tip: The rating moved up 0.9 stars because of volume, not just because the reviews were positive. Adding 42 reviews to a 47-review base changes the denominator enough to move the average quickly. That is why velocity matters: a thin review base is a fast mover.

The 92 percent of submitted reviews were 5-star, which tells me the sentiment gate was working. The 8 percent that were not 5-star were 4-star reviews, and those are almost as valuable because they look authentic. A perfect profile reads as fake.

The profile calls were the surprise. I expected reviews to help rankings over months, not to move the phone in weeks. But the recency signal pushed the profile into the top 3 of the local pack for “AC repair Plano” by week 6, and the calls followed. The client’s office manager stopped printing review cards entirely. The same review-velocity mechanics are why the home service cost cuts I documented for another DFW client compound over time instead of decaying.

When This Works (And When It Does Not)

This system works when three things are true:

  1. The service itself is good. Automation cannot manufacture reviews for a company that leaves customers angry. The sentiment gate protects the profile, but if 40 percent of jobs generate complaints, the gate will skip 40 percent of requests and the velocity will never build.
  2. The business has consistent job volume. This client does 15 to 25 jobs per week. That volume generates enough review requests to move the rating inside 60 days. A business doing 4 jobs a month will see the same mechanics but much slower results.
  3. The profile is the front door. If customers find the business through the map pack and the profile, this system is your highest-leverage marketing investment. If the business is a national chain with a call center, the local angle matters less.

It does not work when the business treats reviews as a vanity metric. Reviews are a ranking signal, a conversion signal, and an ad quality signal. Google Ads quality score, the ads feedback loop I wrote about in the lead generation system post, and the local pack all read the same profile. A profile with fresh reviews makes the paid side cheaper too, because the landing experience converts better.

This is why I now include the review engine in every marketing automation services engagement for home service companies. The paid system and the local system feed each other. Reviews lower ad costs, ads feed the profile with more customers, and the loop compounds.

What I’d Do Differently

Three lessons from this build.

First, I would install the sentiment gate before the first message, not after. In the first 10 days, before the gate was in place, one angry customer left a 1-star review after receiving a request for a job that had gone sideways. The gate was live by day 11, but that review is still on the profile. It is one of the 11 non-5-star reviews now, and it would not be there if I had sequenced the build correctly.

Second, I would add the second SMS from the start. The follow-up message cost almost nothing and lifted response rate from 23 to 41 percent. I built it as a phase 2 improvement because I wanted to avoid being annoying. The data said the opposite: silent customers are not annoyed by a single follow-up, they just forgot.

Third, I would capture the technician’s photo workflow earlier. The profile photo update was the lowest-tech part of the system and it made a visible difference in profile engagement. One photo per install, uploaded weekly, keeps the profile looking alive. I underrated it because it was not automation.

The lead generation services I build for clients now start with this local layer before the paid layer, because a business that shows up well organically gets more out of every ad dollar. If you are running a home service business in the DFW area and your profile is stuck at 3.8 stars, the fastest fix in your control is the review engine. The n8n workflow above is the whole build, and the SMS costs less than a single ad click.


If you want this running in your business, the custom n8n workflows page covers how I scope these builds, or get in touch and I will walk you through what the first 30 days look like.

hvaclocal-marketinggoogle-business-profilereviewsn8nhome-servicesdfwgrowth
Share: