HVAC Business Quote Calculator
It's 95 degrees and someone's AC just died. They're searching "AC repair near me" and landing on three websites. One says "call for quote." One has a contact form. Yours shows them an instant estimate and lets them book emergency service. Who gets the job?
HVAC is time-sensitive. When heating or cooling fails, people want answers fast. A quote calculator that shows pricing immediately - and lets them select urgency level - converts visitors into booked jobs while competitors are still playing phone tag.
This guide covers building quote forms for the three main HVAC service types: repair, installation, and maintenance. Each has different pricing models and information requirements.
Service Type Selection
Start by understanding what the customer needs. The rest of the form adapts based on this choice.
form.addRow(row => {
row.addDropdown('serviceType', {
label: 'What do you need?',
options: [
{ id: 'install', name: 'New Installation' },
{ id: 'repair', name: 'Repair Service' },
{ id: 'maintenance', name: 'Maintenance/Tune-Up' },
{ id: 'replacement', name: 'System Replacement' }
],
isRequired: true
});
});Four main categories cover most HVAC work: new installations, repairs, maintenance tune-ups, and system replacements. Replacement is similar to installation but signals existing equipment needs to go.
System Type: Context Matters
What kind of system they have - or want - changes the entire quote. And the options should change based on whether it's a repair or installation.
form.addRow(row => {
row.addDropdown('systemType', {
label: 'System Type',
options: () => {
const service = form.dropdown('serviceType')?.value();
// Different options for install vs repair
if (service === 'install') {
return [
{ id: 'central-ac', name: 'Central Air Conditioning' },
{ id: 'mini-split', name: 'Ductless Mini-Split' },
{ id: 'heat-pump', name: 'Heat Pump' },
{ id: 'furnace', name: 'Furnace' },
{ id: 'full-system', name: 'Complete HVAC System' }
];
}
return [
{ id: 'central-ac', name: 'Central AC' },
{ id: 'mini-split', name: 'Mini-Split' },
{ id: 'heat-pump', name: 'Heat Pump' },
{ id: 'furnace', name: 'Furnace/Heater' },
{ id: 'boiler', name: 'Boiler' },
{ id: 'not-sure', name: 'Not Sure' }
];
},
isRequired: true
});
});For repairs, include "Not Sure" - many homeowners don't know what type of system they have. For installations, list what you offer with full names so they understand the options.
Pro tip
Mini-splits are increasingly popular for additions and older homes without ductwork. If you install them, make sure they're prominent in your options - they're often higher margin than central AC work.
Home Size for Installations
Equipment sizing depends on square footage. For installations and replacements, you need to know the home size to quote accurately.
form.addRow(row => {
row.addDropdown('homeSize', {
label: 'Home Size (sq ft)',
options: [
{ id: 'small', name: 'Under 1,000 sq ft' },
{ id: 'medium', name: '1,000 - 2,000 sq ft' },
{ id: 'large', name: '2,000 - 3,000 sq ft' },
{ id: 'xlarge', name: '3,000 - 4,000 sq ft' },
{ id: 'xxlarge', name: 'Over 4,000 sq ft' }
],
isRequired: true,
isVisible: () => {
const service = form.dropdown('serviceType')?.value();
return service === 'install' || service === 'replacement';
}
});
});Use ranges instead of exact numbers. Most homeowners know approximately how big their house is, but can't tell you it's exactly 2,347 square feet. Ranges are easier to answer and good enough for initial quotes.
Notice the isVisible condition - this field only appears for installations and replacements. Repairs don't need it.
Repair Details
For repairs, you need to understand the problem. A checkbox list of common symptoms helps customers describe the issue even if they don't know HVAC terminology.
const repairSection = form.addSubform('repairDetails', {
title: 'Problem Details',
isVisible: () => form.dropdown('serviceType')?.value() === 'repair'
});
repairSection.addRow(row => {
row.addCheckboxList('symptoms', {
label: 'What\'s happening?',
options: [
{ id: 'not-cooling', name: 'Not cooling' },
{ id: 'not-heating', name: 'Not heating' },
{ id: 'strange-noise', name: 'Strange noises' },
{ id: 'leaking', name: 'Leaking water' },
{ id: 'bad-smell', name: 'Bad smell' },
{ id: 'high-bills', name: 'High energy bills' },
{ id: 'not-starting', name: 'Won\'t turn on' }
]
});
});
repairSection.addRow(row => {
row.addTextarea('description', {
label: 'Describe the issue',
placeholder: 'When did it start? Any recent changes?',
rows: 3
});
});"Not cooling" tells you more than "it's broken." Combined with system type, you can often diagnose the likely issue before the tech arrives. Strange noises might be a fan motor. Leaking usually means a drain line or refrigerant issue. High bills suggest efficiency problems.
Emergency vs. Scheduled Service
This is where HVAC businesses often leave money on the table. Emergency service costs more - but customers expect that. What they want is clarity about what they're paying for.
form.addRow(row => {
row.addRadioButton('urgency', {
label: 'How urgent is this?',
options: [
{ id: 'emergency', name: 'Emergency - Need help today' },
{ id: 'soon', name: 'Soon - Within 2-3 days' },
{ id: 'scheduled', name: 'Flexible - Schedule at convenience' }
],
defaultValue: 'scheduled',
isVisible: () => form.dropdown('serviceType')?.value() === 'repair'
});
});Three tiers work well: emergency (today), soon (2-3 days), and flexible (whenever works). Price each tier transparently. Someone whose AC died in August will happily pay the emergency fee. Someone with a minor issue will choose the cheaper scheduled option.
Installation Pricing
Installation quotes depend on system type and home size. A pricing matrix lets you show accurate estimates instantly.
const installPrices: Record<string, Record<string, number>> = {
'central-ac': { small: 3500, medium: 4500, large: 5500, xlarge: 7000, xxlarge: 9000 },
'mini-split': { small: 2000, medium: 3000, large: 4500, xlarge: 6000, xxlarge: 8000 },
'heat-pump': { small: 4000, medium: 5500, large: 7000, xlarge: 8500, xxlarge: 10000 },
'furnace': { small: 2500, medium: 3500, large: 4500, xlarge: 5500, xxlarge: 7000 },
'full-system': { small: 6000, medium: 8000, large: 10000, xlarge: 13000, xxlarge: 16000 }
};
form.addRow(row => {
row.addPriceDisplay('installEstimate', {
label: 'Estimated Cost',
prefix: 'Starting at',
variant: 'highlight',
computedValue: () => {
const service = form.dropdown('serviceType')?.value();
if (service !== 'install' && service !== 'replacement') return null;
const system = form.dropdown('systemType')?.value();
const size = form.dropdown('homeSize')?.value();
if (!system || !size || !installPrices[system]) return null;
return installPrices[system][size];
},
isVisible: () => {
const service = form.dropdown('serviceType')?.value();
return service === 'install' || service === 'replacement';
}
});
});Use "Starting at" as a prefix - installations have variables you can't capture in a simple form (ductwork condition, electrical requirements, accessibility). The starting price sets expectations while leaving room for the actual quote after assessment.
See HVAC pricing calculators in action.
Repair Pricing: Service Calls
Repair pricing is tricky - you can't quote the repair until you diagnose the problem. But you can quote the service call fee based on urgency.
const serviceFees: Record<string, number> = {
'emergency': 150,
'soon': 89,
'scheduled': 69
};
form.addRow(row => {
row.addPriceDisplay('serviceFee', {
label: 'Service Call Fee',
computedValue: () => {
const urgency = form.radioButton('urgency')?.value() || 'scheduled';
return serviceFees[urgency];
},
isVisible: () => form.dropdown('serviceType')?.value() === 'repair'
});
});
form.addRow(row => {
row.addTextPanel('repairNote', {
computedValue: () => {
const urgency = form.radioButton('urgency')?.value();
if (urgency === 'emergency') {
return '⚡ Emergency service available 24/7. Fee waived if repair exceeds $300.';
}
return 'Service fee applied to repair cost if you proceed with work.';
},
isVisible: () => form.dropdown('serviceType')?.value() === 'repair'
});
});The note about waiving the fee for larger repairs is a common practice that removes friction. Customers don't feel like they're paying twice - the diagnostic fee rolls into the repair cost.
Maintenance Plans
Maintenance is where HVAC businesses build recurring revenue. Your form should make the value of annual plans obvious.
const maintenanceSection = form.addSubform('maintenance', {
title: 'Maintenance Options',
isVisible: () => form.dropdown('serviceType')?.value() === 'maintenance'
});
maintenanceSection.addRow(row => {
row.addRadioButton('planType', {
label: 'Service Type',
options: [
{ id: 'tuneup', name: 'One-Time Tune-Up - $99' },
{ id: 'annual', name: 'Annual Plan (2 visits) - $169/year' },
{ id: 'premium', name: 'Premium Plan (2 visits + priority) - $249/year' }
],
defaultValue: 'tuneup'
});
});
maintenanceSection.addRow(row => {
row.addTextPanel('planBenefits', {
computedValue: () => {
const plan = form.radioButton('planType')?.value();
if (plan === 'annual') {
return '✓ Spring AC tune-up\n✓ Fall heating tune-up\n✓ 10% off repairs';
}
if (plan === 'premium') {
return '✓ Spring AC tune-up\n✓ Fall heating tune-up\n✓ 15% off repairs\n✓ Priority scheduling\n✓ No overtime charges';
}
return '✓ Full system inspection\n✓ Filter replacement\n✓ Performance check';
}
});
});Show what's included in each tier. The premium plan's "no overtime charges" benefit pays for itself if they ever need emergency service. Priority scheduling matters when it's 100 degrees and everyone's AC is struggling.
Seasonal Considerations
HVAC is seasonal. Your form can acknowledge this:
- Summer - AC repairs surge. Emphasize same-day service.
- Winter - Heating repairs and furnace tune-ups. Promote heating maintenance.
- Spring/Fall - Best time for installations. Mention off-season discounts.
Consider showing different messaging or promotions based on the current month. "Book your AC tune-up now before the summer rush" converts better in April than August.
Service Area
HVAC is local. You probably have a service area, and customers outside it waste everyone's time. Use address validation to check before they submit.
See our address validation guide for how to implement service area checking with distance-based pricing for customers at the edge of your territory.
Complete Example
Here's a working HVAC quote form covering the main service types:
export function hvacQuote(form: FormTs) {
form.setTitle(() => 'HVAC Service Quote');
// Service type selection
form.addRow(row => {
row.addDropdown('serviceType', {
label: 'What do you need?',
options: [
{ id: 'repair', name: 'Repair Service' },
{ id: 'maintenance', name: 'Maintenance/Tune-Up' },
{ id: 'install', name: 'New Installation' }
],
isRequired: true
});
});
// System type
form.addRow(row => {
row.addDropdown('systemType', {
label: 'System Type',
options: [
{ id: 'central-ac', name: 'Central AC' },
{ id: 'mini-split', name: 'Mini-Split' },
{ id: 'heat-pump', name: 'Heat Pump' },
{ id: 'furnace', name: 'Furnace' },
{ id: 'not-sure', name: 'Not Sure' }
],
isRequired: true
});
});
// Urgency for repairs
form.addRow(row => {
row.addRadioButton('urgency', {
label: 'Urgency',
options: [
{ id: 'emergency', name: 'Emergency ($149 call)' },
{ id: 'soon', name: '2-3 days ($89 call)' },
{ id: 'flexible', name: 'Flexible ($69 call)' }
],
defaultValue: 'flexible',
isVisible: () => form.dropdown('serviceType')?.value() === 'repair'
});
});
// Contact info
const contact = form.addSubform('contact', { title: 'Contact Information' });
contact.addRow(row => {
row.addTextbox('name', { label: 'Name', isRequired: true });
row.addTextbox('phone', { label: 'Phone', isRequired: true });
});
contact.addRow(row => {
row.addEmail('email', { label: 'Email', isRequired: true });
});
form.configureSubmitButton({ label: 'Get Quote' });
}This handles the core flow: service type, system type, urgency for repairs, and contact info. Customize the pricing, add your service area, and you've got a working quote calculator.
What Happens After Submission
A quote form is step one. What happens next matters just as much:
- Instant confirmation - Show what they requested and expected timeline
- Quick follow-up - For emergencies, call within minutes
- Detailed quote - Email a professional PDF quote for installations
- Easy booking - Let them schedule directly if possible
The form captures the lead. Your follow-up process converts it to revenue.
Common Questions
How accurate should online HVAC quotes be?
For repairs, quote the service call fee accurately - the repair cost comes after diagnosis. For installations, 'starting at' prices should be within 20% of typical final quotes. If your online estimates are consistently way off, customers lose trust. Update your pricing matrix regularly.
Should I show installation prices online?
Yes, with appropriate framing. Use 'starting at' or 'typical range' language. Customers researching HVAC installations want ballpark numbers. If you hide pricing, they'll find a competitor who doesn't. Transparency builds trust.
How do I handle after-hours emergency requests?
Show clear pricing for emergency service (it should cost more). If you offer 24/7 service, say so prominently. If you don't, set expectations: 'Emergency requests received after 6 PM will be contacted first thing in the morning.' Honesty prevents frustration.
Should I require all fields?
Require: service type, system type, contact info. Optional: problem description, preferred scheduling. The more friction, the fewer completions. You can gather additional details on the follow-up call. Get the lead first.
How do I handle commercial vs residential?
Either have separate forms or add a property type question early that changes the entire flow. Commercial HVAC has different equipment, pricing, and urgency considerations. Mixing them in one form creates confusion.