AI Marketing for HVAC: A Worked Spring and Autumn Tune-Up Campaign

Coding Liquids tutorial cover featuring Sagnik Bhattacharya for AI Marketing for HVAC: A Worked Spring and Autumn Tune-Up Campaign.
Coding Liquids tutorial cover featuring Sagnik Bhattacharya for AI Marketing for HVAC: A Worked Spring and Autumn Tune-Up Campaign.

Give the AI the layout of your customer export, not the customers. It writes the rules that split past customers into segments, drafts an email and a text for each segment, and lays out dated spring and autumn send schedules. You add the price, dates and booking link, check every claim and track bookings by segment.

The plan only works if it is sized to the diary. A campaign that fills the schedule three weeks before the first hot spell is a success. One that books 200 visits into a week with 80 free slots is a service problem you created yourself. So the schedule below is built backwards from technician capacity, and each wave goes out only when there is room.

Follow me on Instagram@sagnikteaches

The firm in this walk-through is invented: six vans (four on service work, two on installs), 2,450 customer records in its field-service app, 340 maintenance-plan members and a $129 tune-up. Replace every figure with your own. Nothing here is a benchmark, and none of the copy promises your customers a saving.

Connect on LinkedInSagnik Bhattacharya

AI never needs to see a customer name or address for any of this. Segmenting is a spreadsheet job, and the copy uses placeholders such as [first name] that your email tool fills in. That keeps the customer list out of the chat window altogether.

Subscribe on YouTube@sagnikteaches

Set Week 0 from last year's calls, then cap the list by capacity

Week 0 is the week your phones usually fill with "not cooling" calls (for autumn, "no heat"). Pull last year's jobs from your app, count repair calls by week and mark the first week the count reaches double your quiet-season average. Every date in the schedule counts back from there. A pivot table does this; no AI is needed.

Next, work out how many tune-ups you can actually sell. The invented firm has four technicians on service work, each finishing four tune-ups a day when routes are sensible. That is 4 x 4 x 5 = 80 visits a week, or 640 over an eight-week window. Repairs and callbacks still need room, so hold back 40%: 640 x 0.6 = 384 sellable slots, about 48 a week.

That 384 is the ceiling. It sets how fast you release each wave, and it is the number you check before every send. A tune-up promised for next week and delivered in five costs more goodwill than the campaign earns.

Clean the export, then sort it into seven segments

Export every customer with the fields the rules need: plan status, next visit due, install date, last tune-up, last repair, equipment age, and the email and text consent flags. Then remove the records you should not contact, or do not need to contact this season, in this order.

StepRecords removedRecords left
Raw export from the app02,450
Duplicates (same address and surname)632,387
Commercial accounts (handled separately)1602,227
Open complaint or unpaid invoice222,205
Do-not-contact flag or email opt-out382,167
No usable email address or text consent1871,980
Plan members whose visit is due later (held for a later wave)281,952
Non-members tuned in the last 12 months (not due)1221,830

The 1,830 left are sorted by rules, not by feel. A customer belongs to the first rule they meet, so nobody receives two campaigns at once. The counts below are the invented firm's.

SegmentRuleRecordsWhat they need to hear
S1Plan member with a visit due in the next 60 days312Your visit is due; here are the times
S2New install in the last 12 months74First-year check and warranty paperwork
S3Plan lapsed in the last 24 months96One-off price now, or rejoin the plan
S4Equipment 10 or more years old281An honest age check, no replacement push
S5Last tune-up 12 to 24 months ago, no plan398It has been a while; price and dates
S6Repair in the last 24 months, no tune-up in that time311We fixed one thing; here is the full check
S7Everyone else (nothing for 24 months or more)358A short reintroduction, test batch only

The seven add up to 1,830. Two of them deserve a note. S4 is where honesty matters: a 14-year-old system gets a plain check-up message, and any replacement conversation waits for what the technician finds (the ageing-systems outreach tutorial covers that part separately). S7 is the smallest bet. People who bought nothing for two years may not remember you, so they get one short email and a test batch.

Ask AI for the formula, and test it on invented rows

Do not paste the customer list into a chat window. You cannot see which rows an assistant skipped, and the segmenting does not need names anyway. A formula applies one rule to every row and can be audited. Give the assistant your column headings and ask for the formula plus test rows.

I run an HVAC company. My customer export has these columns:
A id | B plan_status (Member, Lapsed, None; Lapsed means ended in the last 24 months)
C next_visit_due (date, members only) | D install_date | E last_tuneup (date, may be blank)
F last_repair (date, may be blank) | G equipment_age_years | H excluded (Y or N)

Write one Google Sheets formula for column I that gives each row the FIRST rule it meets:
X = excluded; S1 = member due within 60 days; HOLD = member due later;
SKIP = non-member tuned in the last 12 months; S2 = installed in the last 12 months;
S3 = lapsed member; S4 = equipment age 10 or more; S5 = last tune-up 12 to 24 months ago;
S6 = repair in the last 24 months and no tune-up in that time; S7 = everything else.
Members must never fall into SKIP or S2 to S7. Use EDATE and TODAY.
Then give me ten test rows, including edge cases, with the segment each should get,
so I can check the formula by hand.

What comes back should look something like this. Compare it with your own headings before you trust it.

=IF(H2="Y","X",IF(B2="Member",IF(C2<=TODAY()+60,"S1","HOLD"),IF(AND(E2<>"",E2>EDATE(TODAY(),-12)),"SKIP",IF(D2>=EDATE(TODAY(),-12),"S2",IF(B2="Lapsed","S3",IF(G2>=10,"S4",IF(AND(E2<>"",E2>=EDATE(TODAY(),-24)),"S5",IF(AND(F2<>"",F2>=EDATE(TODAY(),-24)),"S6","S7"))))))))

Then run the test rows. These ten are the ones that matter for this build, and the expected answer is written next to each.

  • T1 Member, visit due in 30 days: S1.
  • T2 Member, visit due in 90 days: HOLD, to be mailed in a later wave.
  • T3 Member with a 14-year-old system: S1 or HOLD, never S4.
  • T4 Non-member, installed five months ago: S2.
  • T5 Lapsed member, last tune-up 20 months ago: S3, not S5.
  • T6 Non-member, 12-year-old system, tuned 18 months ago: S4, not S5.
  • T7 Non-member, six-year-old system, tuned 18 months ago: S5.
  • T8 Non-member, no tune-up, repair 10 months ago: S6.
  • T9 Any customer with the excluded flag set to Y: X.
  • T10 Non-member tuned six months ago: SKIP, not S5.

In the invented firm's first version of the formula, the member test sat after the age test. A member with a 14-year-old system (T3) landed in S4 and would have been offered the non-member $129 price, which is exactly the email that makes a loyal customer ask why they pay for a plan. Ten test rows caught it before anything was sent. On build day, paste the finished column as values so TODAY() stops moving people between segments.

The spring schedule: eight weeks, released in waves

The schedule counts back from Week 0, and each wave has a gate. If more than 80% of the next fortnight's sellable slots are already booked, the wave waits a week.

WeekSendWhoChannelGate before sending
-9BuildAllNoneExport cleaned, formula tested, copy approved by the owner
-8Wave 1S1 + S2 (386)EmailNext 14 days under 80% booked
-7ReminderS1 + S2 who have not bookedTextText consent on file
-6Wave 2S3 + S4 (377)EmailNext 14 days under 80% booked
-5Wave 3S5 (398)EmailNext 14 days under 80% booked
-4ReminderS4 + S5 who clicked but did not bookTextText consent on file
-3Wave 4S6 (311)EmailNext 14 days under 80% booked
-2Public postEveryoneProfile and websiteThe offer has real start and end dates
-1Test batchS7, 120 of 358EmailOnly if slots are open
0CloseNoneNoneBooking link switches to "next opening"; debrief starts

The gate is the one part no AI can do for you, because it depends on a diary only you can see. Suppose a third of wave 1 books (an assumption to replace with your own last-year rate). That is 386 / 3, about 129 visits, nearly three weeks of the 48 weekly slots. So the gate column matters more than the week numbers: if wave 1 fills the next fortnight, wave 2 waits, even when the table says Week -6. If surge capacity is your real worry, the summer surge checklist goes further.

Each customer gets one email and at most one text per season. Plan members get one extra reminder, because they are owed the visit, and the 28 held back by the formula join as their visit dates come inside 60 days. The member reminders themselves are worth automating once; the plan reminders tutorial shows how, and the schedule above then only has to handle everyone else.

The Week -2 public post is a Google Business Profile offer. As Google's help page describes it, an offer post needs a title, dates and times, posts are archived after six months unless a date range is set, and a post with a phone number in its description might be rejected, so leave the number out of the text and let the profile carry it. Keep the offer wording identical to your emails, word for word, so a customer who sees both reads one offer. The rest of the profile, from service areas to review replies, is covered in the HVAC profile tutorial.

One email, drafted by AI and then corrected

Ask an assistant for "a spring AC tune-up email" and give it no facts, and it will write something like the first draft below. This is an illustrative draft, and it is the reason the review step exists.

Subject: Protect your home from costly AC breakdowns this summer!

Hi [first name], summer is around the corner, and a well-maintained AC can save you up to 20% on energy bills and prevent expensive breakdowns. Don't wait until it's too late! Book your annual tune-up today and enjoy peace of mind all season long. Our certified technicians will make sure your system is running at peak efficiency. Limited slots available, so book now!

Every claim in it needs a source you can point to, or it goes. Treat this as a claims ledger and keep one for each campaign.

Line in the draftProblemWhat replaces it
"Save up to 20% on energy bills"No source. A figure you cannot prove is a promise you cannot keep.Delete it. Say what the technician measures.
"Prevent expensive breakdowns"No tune-up can guarantee that."We check the parts on this list and tell you what we find."
"Certified technicians"True only if every technician holds a named credential.Name the credential, or drop the word.
"Peak efficiency"Vague. A measurement only means something if you name it.List the readings you do take.
"Limited slots available"True only if the wave gate says so.State the first available date and the offer end date.
"Don't wait until it's too late"Fear pressure, and it invites complaints.Delete it.

The better prompt hands the assistant the only facts it may use and tells it what to leave out.

Write a spring AC tune-up email for segment S5: customers whose last tune-up with us was
12 to 24 months ago and who are not on a plan.
Facts you may use, and nothing else: price $129 for one system until [date]; the visit takes
[duration]; our [N]-point checklist; written results emailed after the visit; repairs are
quoted before any work; first available visit [date]; booking link [link].
Do not claim energy savings, longer equipment life or fewer breakdowns. Do not use the words
"certified" or "limited". Plain language, under 130 words, subject line under 50 characters.
Use [first name] as the only merge field.

After one round of edits (a friendlier first line, the real checklist items and an unsubscribe sentence), the version that ships looks like this.

Subject: Your AC check: visits from [date], $129

Hi [first name], it has been [months] months since [Company] last looked at your cooling system. A tune-up takes about [duration] and follows our [N]-point checklist: we clean the outdoor coil, check the condensate drain, test the electrical connections and measure how the system is running. You get the results in writing after the visit, and any repair is quoted before we touch anything.

The price is $129 for one system until [date]. We are booking from [first date]; choose a time here: [link]

If you would rather not get these emails, use the unsubscribe link below and we will stop.

Swap in your own checklist items. The ones shown are a plausible list, not a standard.

Three texts, counted

Texting tools bill by message part. Twilio's documentation says any character outside the basic GSM-7 set, including the curly quotes some editors insert on their own, makes the message fall back to an encoding that allows only 70 characters, and a longer message is then split into parts of 67. So type apostrophes on the keyboard or write "it is", and leave emoji out. The three texts below were counted with a five-letter name, a six-letter business name and a 19-character short link merged in.

SegmentTextCharacters
S1 memberHi [first name], your AC plan visit with [Company] is due. Choose a time: [link] Reply STOP to opt out.106
S5 non-memberHi [first name], [Company] here. A tune-up for your AC is $129 until [date]. Book a time: [link] Reply STOP to opt out.122
S4 age checkHi [first name], [Company] here. Your AC is [age] years old. A $129 check shows how it is running. Book: [link] Reply STOP to opt out.134

All three fit one part. Add a single emoji to the first and its 106 characters no longer fit in 70, so it goes out as two parts and is billed as two. Longer first names and links eat the spare characters, so check the longest name in your list, not the average one.

Marketing texts go only to customers who agreed to receive them, and every one ends with a way to stop. Consent and opt-out rules differ from place to place, so ask your adviser or your texting provider's compliance team what applies to you before the first send.

Offer wording: what to promise and what to leave out

OfferWording that holds upWhat AI tends to add
Fixed-price tune-up"$129 for one system. [N]-point checklist, written results. Repairs are quoted before any work.""Up to 30% off", or "free diagnostic" when it is not free.
Plan renewal"Plan members book first, from [date]. [The benefit written in your agreement].""Exclusive savings" with no figure behind it.
Second system, same visit"$[X] for a second system at the same visit.""Best price in town."
Customer thank-you"If you recommend us and they book, we will [thank-you you can afford]."A reward tied to a review, which Google's review policy does not allow.
Deadline"Price holds until [date]. After that it is $[normal price].""Act now" with no real end date.

Two rules cover most of it. Every offer has an end date, and you keep the end date; extending a "last chance" teaches customers to wait. And keep review requests apart from offers: Google's review policy does not allow payment, discounts or free services offered in exchange for a review, and it rules out asking only the customers you expect to be happy.

Track bookings by segment, then do the break-even sum

Give every wave an ID, such as SP-S5-W3 for spring, segment 5, wave 3. Put it in three places: the booking link as a tag on the end of the address, the "lead source" or "campaign" field in your app, and the phone script ("Did you get a text or email from us?"). Phone bookings are the ones that go missing, because the caller does not remember which message they saw.

https://example.com/book?utm_source=email&utm_medium=tuneup&utm_campaign=sp-s5-w3

Then keep one row per wave in a plain sheet. The numbers below are invented to show the layout and are not benchmarks.

WaveWhoSentBookedDone so farUnsubscribed
SP-W1S1 + S2386118742
SP-W2S3 + S437741123
SP-W3S53985264

Watch the unsubscribe and complaint columns after every wave. If either jumps against the previous wave, pause and read the replies before the next send. Google's sender guidelines also ask all senders to set up SPF or DKIM authentication for their sending domain and to keep reported spam rates below 0.30%, aiming under 0.10%, so ask whoever hosts your email to confirm authentication is on before the first send. The guidelines page has the detail. Mailchimp's free plan, for example, caps the list at 250 contacts and 500 sends a month with no scheduling or automation flows, which is far below 1,830 contacts, so the invented firm would send from its app's own email or a paid tier (paid plans start from about $13 a month and rise with list size).

Now the break-even sum, which keeps the campaign honest. Say the direct cost of a tune-up visit (technician time, vehicle, consumables) is $70, so each $129 visit contributes $59. The campaign costs about $60 for the email tool across two months, $30 for texts and eight hours of owner time valued at $35 an hour, which is $280. That is $370 in all, and 370 / 59 is 6.3, so seven completed tune-ups pay for it. Follow-on repairs and replacements are upside; leave them out of the sum so the campaign has to justify itself on tune-ups alone.

Autumn: reuse the build and change six things

The cleaned export, the formula and the schedule table all carry over. Rebuild the segments with new dates, then change these.

What changesSpring (cooling)Autumn (heating)
Week 0First week "not cooling" calls doubleFirst week "no heat" calls double
Checklist wordingOutdoor coil, condensate drain, electrical checks, readingsBurner and ignition, flue, heat-pump heating mode, filter
Equipment named in the copyAC or heat pumpFurnace, boiler or heat pump, matched to the record
S4 age ruleAge of the cooling equipmentAge of the heating equipment, which may differ
Safety wordingNone neededMention carbon monoxide testing only if technicians test on every visit
Wave sizesSet from last year's booking rate, or a cautious guess if you have noneSet from the spring results, segment by segment

The autumn age-check text shows how much the tone matters. "Your furnace is [age] years old. We are not saying it needs replacing; the check shows how it is running today and we will tell you plainly what we find." That sentence invites a booking without sounding like a replacement pitch, and it is only safe to send if your technicians really do give an honest answer on the day.

After the campaign, paste the totals table (no customer data) into an assistant and ask for three observations and one change per segment. Recompute any figure it quotes before you act on it, then save the observations next to the schedule so the next season starts from them.

The pre-send review, and the mistake that still gets through

Someone other than the writer reads each message aloud, with this list beside them.

  1. Price, dates and booking link match the offer sheet.
  2. Every factual claim is in the claims ledger with its source.
  3. The wave gate has been checked against today's diary.
  4. Every recipient has consent for the channel used, and every message has a working way to stop.
  5. Merge fields render properly: no "Hi ," and no unfilled brackets.
  6. The equipment named in the copy matches the customer's record.
  7. Test sends have arrived on a phone and in two different inboxes, with the links tested.
  8. One named person owns the inbox for replies, and the offer sheet is in front of them.

The mistake that still gets through is the equipment mismatch. An autumn email that says "your furnace" goes to a customer whose home has a heat pump, because the export lists the old furnace the heat pump replaced. Fix it in the data, not the copy: segment by current equipment type, and test with five real records from each segment before the wave goes out.

Questions HVAC owners ask before the first send

How do I handle customers with more than one system?

Export one row per system rather than per customer and run the segment formula on those rows. Then collapse to one row per customer by keeping the highest-priority segment, so S1 beats S2 and so on. The customer receives a single message that names both systems, and your second-system price applies to the visit instead of two separate offers.

Do commercial customers belong in the same campaign?

No. Offices, landlords and property managers buy on contracts, the contact is a person rather than a household, and a $129 homeowner offer reads oddly to them. Give commercial accounts their own list and wording built around service agreements and visit records, and have the account owner approve it before it goes out.

Is a tune-up campaign worth running if the diary is already full?

Not for the whole list. If the next three weeks are booked solid, mail only plan members who are owed a visit and hold everything else until the diary has room. The campaign earns its keep in the shoulder weeks, when technicians would otherwise be waiting for repair calls.

What should happen when a customer replies with a complaint?

A person answers, not the assistant. Route replies to one named inbox, read them daily during the campaign, and phone anyone who sounds upset. A complaint about an old repair is also a reason to remove that customer from later waves until it is resolved.

Further reads

Sources: Google email sender guidelines (Workspace Admin Help); Twilio documentation on UCS-2 character encoding; Google Business Profile Help on adding posts; Google's prohibited and restricted content policy on reviews; Mailchimp's pricing page (Free plan limits and USD starting prices, checked October 2026).

Want your tune-up campaign built once and reused?

On a 1:1 call we'll go through your customer export and booking app, pick the segments worth mailing first and decide which steps are worth automating.

Book a 1:1 call with me