Voice AI for Roofing Contractors: Full Retell AI Setup Guide
I deployed a voice AI agent for a roofing contractor client that answers calls 24/7, qualifies storm damage leads, and books inspection appointments — all without a human on the line. Here's the exact setup: Retell AI + Claude, the conversation flows, webhook integration, and what it actually costs.
Why Roofing Contractors Need Voice AI Right Now
Here's the roofing industry problem nobody talks about in automation circles: during hail season, a single storm cell can hit a metro area and generate hundreds of homeowner calls in a 48-hour window. Every contractor in the market is chasing the same leads. The first contractor to call back wins the job. The contractor who calls back two hours later loses it to someone else.
My client runs a 12-person roofing operation out of North Texas. Before we deployed the voice AI, their inbound call answer rate was around 40% during business hours — worse on weekends, nonexistent at night. They had a voicemail. Homeowners with fresh storm damage don't leave voicemails. They hang up and call the next number on Google.
The math on missed calls is brutal. In Texas, a residential roof replacement runs $8,000–$25,000 depending on size and material. An insurance claim job often pays on the higher end because the homeowner isn't cost-sensitive — the insurer is paying. Miss five of those calls in a storm week and you've lost $50,000–$100,000 in potential revenue. Every week.
An answering service costs $300–$600/month and still requires someone to call leads back. It doesn't qualify them, doesn't book inspections, doesn't work at 11pm when a homeowner notices their ceiling is wet. A voice AI agent does all three.
What we wanted the agent to do:
- Answer every inbound call within 2 rings, 24/7
- Qualify lead type: storm damage, active leak, new roof quote, or wrong number
- Collect key info: address, damage type, homeowner vs. renter, preferred inspection time
- Book inspection slots directly into the contractor's calendar
- Send lead data to GoHighLevel for follow-up pipeline
- Trigger SMS confirmation to the homeowner immediately after the call
We hit all six. Here's how.
The Stack: Retell AI + Claude + n8n + GoHighLevel
Before we get into configuration, here's the full picture of what's running and why each piece is there.
Retell AI is the voice infrastructure layer. It handles telephony (Twilio or their own numbers), speech-to-text, text-to-speech, and the real-time conversation loop. The reason I chose Retell over building directly on Twilio + Deepgram + ElevenLabs is latency. Retell's average response latency is under 800ms, which is the threshold where voice conversations feel natural. Build it yourself and you're fighting that number constantly.
Claude is the LLM that drives the conversation. Retell supports multiple LLM backends — OpenAI, Anthropic, custom WebSocket. I use Claude because it handles nuanced conversations better than GPT-4 for this use case. Roofing homeowners are stressed, sometimes upset, and often unclear about what kind of damage they have. Claude reads context and adapts without getting confused. It also respects constraints reliably — telling it "never quote prices" actually sticks across a conversation.
n8n handles the webhook orchestration. When a call ends, Retell fires a webhook. n8n receives it, parses the call summary, extracts lead data, and routes it to GoHighLevel. I self-host n8n, so the workflow automation is essentially free.
GoHighLevel (GHL) is the CRM. Every roofing contractor I work with is already on GHL or a similar CRM. The lead hits GHL as a new contact with tags for damage type, inspection availability, and call recording link.
// Architecture flow
Inbound call
↓
Retell AI (telephony + STT/TTS)
↓
Claude (LLM via Retell WebSocket)
↓
End-of-call webhook
↓
n8n (parse + route)
↓
GoHighLevel (contact + pipeline)
+ SMS confirmation to homeownerSetting Up Retell AI: Agent Configuration
Log into Retell, create a new agent, and select LLM Agent (not Simple Agent — Simple Agent doesn't support custom LLM prompts). Here's the configuration that matters:
Agent Config
// Retell agent config (from dashboard or API)
{
"agent_name": "Alex - Summit Roofing",
"voice_id": "11labs-Josh", // ElevenLabs Josh — professional, calm
"language": "en-US",
"interruption_sensitivity": 0.4, // Lower = less easily interrupted
// Homeowners ramble — you want low
"max_call_duration_seconds": 600, // 10 min cap
"end_call_after_silence_ms": 5000, // End call after 5s silence
"enable_backchannel": true, // "Mm-hmm", "got it" responses
"backchannel_frequency": 0.7,
"reminder_trigger_ms": 10000, // Prompt user if silent 10s
"reminder_max_count": 2,
"llm_websocket_url": "wss://api.retellai.com/llm-websocket/claude-3-5-sonnet"
}The interruption_sensitivity setting is one I wish the docs explained better. At 0.8 (default), the agent stops talking the moment a caller makes a sound — including "uh-huh" or clearing their throat. For roofing homeowners explaining storm damage, this creates a fragmented conversation. Drop it to 0.4 and the agent completes its sentence before yielding the floor. Much more natural.
Voice Selection
For contractor businesses, voice matters more than most people think. The homeowner on the phone just had their roof damaged by a hailstorm. They're anxious. A voice that sounds robotic or overly chipper is a red flag. I tested five voices:
- Josh (ElevenLabs) — Warm, slightly formal. Sounds like a service manager. ✓ Best for roofing
- Rachel (ElevenLabs) — Professional, clear. Good alternative if client prefers female voice
- Adam (ElevenLabs) — Too casual for a contractor context
- Retell built-in voices — Noticeably more robotic. Skip.
ElevenLabs voices cost slightly more per character but the quality difference is significant enough to justify it, especially for a high-ticket service business.
Writing the System Prompt: The Most Important Part
The system prompt is where 80% of the work lives. Get this wrong and no amount of telephony config fixes it. Here's the full prompt I use, with annotations:
You are Alex, a friendly scheduling coordinator for Summit Roofing.
Your job is to help homeowners who call in about roof damage, leaks,
or new roof quotes by collecting their information and scheduling
a FREE inspection with our team.
## Your Personality
- Warm and professional, not salesy
- Empathetic — callers may be stressed about damage
- Concise — don't over-explain, ask one question at a time
- Confident — you know what you need to collect
## What You Need to Collect
Ask for these in a natural conversation order:
1. Caller's name
2. Property address (where the roof is)
3. Type of issue: storm/hail damage, active leak,
aging roof quote, or other
4. If storm damage: when did it occur? (insurance window)
5. Homeowner or renter? (renters can't authorize work)
6. Best day/time for a free inspection (offer:
weekday mornings, weekday afternoons, Saturday morning)
7. Best callback number (confirm or get a new one)
## Hard Rules
- NEVER quote prices. If asked:
"Our estimator will go over all pricing options during
the free inspection — there's no obligation."
- NEVER say we can guarantee insurance will cover it
- If caller is a renter: explain we need the homeowner
to authorize, offer to call back or take their info
to have our team follow up
- If caller is aggressive or upset: stay calm,
acknowledge their frustration, offer to have a
manager call them back
- If it's clearly a wrong number: apologize politely and end
## End of Call — CRITICAL
When you have all required info OR the caller is ending the call,
output a JSON summary as your final message in this exact format:
{
"summary_type": "lead_capture",
"name": "caller name or null",
"address": "property address or null",
"issue_type": "storm_damage|active_leak|new_roof_quote|other|not_qualified",
"storm_date": "date or null",
"is_homeowner": true/false/null,
"preferred_time": "their preference or null",
"callback_number": "number or null",
"notes": "any relevant context"
}The end-of-call JSON is the critical piece that makes the n8n webhook work. Claude outputs this as its final message, Retell captures it in the call transcript, and n8n parses it to create the GHL lead record. Without a structured output format, you're doing NLP on freeform transcripts — messy and error-prone.
Handling Objections
The two objections you'll hit on almost every call:
"I'll call back later" — The agent acknowledges this but still tries to capture contact info. Prompt addition: "No problem at all! Can I take down your number so our team can reach out when you're ready?"
"How much does it cost?" — The agent never quotes prices. This is a hard constraint for two reasons: the contractor doesn't want price shopping, and every job is different. The response: "Roof costs vary a lot based on size and materials — that's exactly why we do the free inspection first, so our estimator can give you an accurate number. Most homeowners are surprised how affordable it ends up being."
Conversation Flow Design
The conversation branches based on the caller's first response. Here's how the four main branches work:
Branch 1: Storm Damage
This is the highest-value lead type. When a caller mentions hail, storm, or wind damage, the agent shifts to an insurance-focused flow. The key questions change: when did the storm occur? (insurance claims have time windows), has the homeowner called their insurance yet?, and is there visible exterior damage or interior water intrusion? This info pre-qualifies the job scope for the estimator.
Branch 2: Active Leak
Urgency qualifier first: is water actively coming in? If yes, the agent flags this as high-priority in the JSON output and mentions same-day or next-day availability. If it's a slow drip or stain from a past event, it routes to the standard inspection scheduling flow.
Branch 3: New Roof Quote
Lower urgency, higher consideration. The agent focuses on collecting address and scheduling an inspection. No insurance angle. The homeowner is comparing quotes so the agent emphasizes the "free, no-obligation estimate" framing.
Branch 4: Wrong Number / Not Interested
Graceful exit. The agent apologizes for the confusion, confirms this is Summit Roofing, and ends the call politely. No hard sell. The JSON output includes "issue_type": "not_qualified" so n8n doesn't create a lead record.
Dynamic Variables for Personalization
Retell supports dynamic variables that you can inject at call start via the API. I use this to pass the caller's name if they're a returning caller in GHL:
// When triggering call via Retell API (for outbound)
// or via webhook lookup for inbound:
{
"dynamic_variables": {
"caller_name": "Sarah", // From GHL lookup by phone
"last_contact_date": "2026-03-10" // If returning lead
}
}
// In system prompt, reference as:
// "If {{caller_name}} is set, greet them by name:
// 'Hi {{caller_name}}, thanks for calling Summit Roofing!'"For new callers, the agent doesn't know their name yet — it collects it in the first minute. For returning callers, the personalized greeting creates a noticeably better first impression.
Webhook Integration: Lead Handoff to GoHighLevel
When a call ends, Retell fires a POST request to your configured webhook URL with the full call data. Here's what the payload looks like and how to handle it in n8n:
Retell Webhook Payload
// POST to your webhook URL after call ends
{
"event": "call_ended",
"call": {
"call_id": "call_abc123",
"agent_id": "agent_xyz789",
"from_number": "+14695551234",
"to_number": "+19725559876",
"start_timestamp": 1742000000000,
"end_timestamp": 1742000210000,
"duration_ms": 210000,
"call_status": "ended",
"transcript": [
{
"role": "agent",
"content": "Hi, thank you for calling Summit Roofing..."
},
{
"role": "user",
"content": "Yeah hi, I had some hail damage last night..."
}
// ... full transcript
],
"call_analysis": {
"call_summary": "Caller reported hail damage from 3/24 storm...",
"custom_analysis_data": {
// This is where your JSON summary ends up
"summary_type": "lead_capture",
"name": "Sarah Thompson",
"address": "1234 Oak Creek Dr, McKinney TX 75070",
"issue_type": "storm_damage",
"storm_date": "2026-03-24",
"is_homeowner": true,
"preferred_time": "weekday morning",
"callback_number": "+14695551234",
"notes": "Homeowner noticed missing shingles and granules in yard"
}
}
}
}n8n Webhook Handler
// n8n Code node — parse Retell webhook and create GHL contact
const body = $input.first().json;
const call = body.call;
const leadData = call.call_analysis?.custom_analysis_data;
// Skip if not a qualified lead
if (!leadData || leadData.issue_type === 'not_qualified') {
return [{ json: { skipped: true, reason: 'not_qualified' } }];
}
// Build GHL contact payload
const ghlContact = {
firstName: leadData.name?.split(' ')[0] || 'Unknown',
lastName: leadData.name?.split(' ').slice(1).join(' ') || '',
phone: leadData.callback_number || call.from_number,
address1: leadData.address || '',
tags: [
'retell-ai-inbound',
leadData.issue_type,
leadData.is_homeowner ? 'homeowner' : 'renter',
leadData.storm_date ? 'insurance-potential' : ''
].filter(Boolean),
customField: {
retell_call_id: call.call_id,
preferred_inspection_time: leadData.preferred_time,
storm_date: leadData.storm_date,
ai_notes: leadData.notes,
call_duration_sec: Math.round(call.duration_ms / 1000)
}
};
return [{ json: ghlContact }];Creating the Contact in GHL
// n8n HTTP Request node — GHL Contact API
// Method: POST
// URL: https://services.leadconnectorhq.com/contacts/
// Headers:
// Authorization: Bearer {{$env.GHL_API_KEY}}
// Version: 2021-07-28
// Content-Type: application/json
// Body (from previous node output):
{
"firstName": "{{$json.firstName}}",
"lastName": "{{$json.lastName}}",
"phone": "{{$json.phone}}",
"address1": "{{$json.address1}}",
"locationId": "{{$env.GHL_LOCATION_ID}}",
"tags": "{{$json.tags}}",
"customFields": [
{
"id": "retell_call_id",
"value": "{{$json.customField.retell_call_id}}"
},
{
"id": "preferred_inspection_time",
"value": "{{$json.customField.preferred_inspection_time}}"
}
]
}After the contact is created, the same n8n workflow triggers an SMS via GHL's messaging API: "Hi [Name]! Thanks for calling Summit Roofing. We have your inspection request for [address]. A team member will confirm your [preferred_time] slot shortly. Questions? Reply to this message."
That SMS fires within 60 seconds of the call ending. It sets expectations, confirms the lead was captured, and gives the homeowner a paper trail. Conversion rates on followed-up leads are noticeably higher when the homeowner gets an immediate confirmation versus waiting for a human to call back.
Cost Breakdown: What This Actually Costs
This is the section most guides skip. Here's the real math for a roofing contractor with 200 inbound calls per month:
| Component | Cost | Notes |
|---|---|---|
| Retell AI | ~$0.09/min | 3.5 min avg = ~$0.32/call → $64/mo for 200 calls |
| Claude API | ~$0.008/call | ~$1.60/mo for 200 calls (minimal) |
| ElevenLabs voice | ~$0.18/1k chars | ~$8–12/mo depending on call length |
| Twilio number | $1.15/mo + $0.0085/min | ~$7/mo for 200 calls at 3.5 min avg |
| n8n (self-hosted) | $0/mo | Or $20/mo cloud. Use self-hosted. |
| GoHighLevel | $97–297/mo | Client likely already paying this |
| Total AI stack | ~$82/mo | Excluding GHL (already paid) |
The ROI Math
Old setup: Answering service at $400/month. Captures about 60% of calls. Doesn't qualify leads. Doesn't book appointments. Requires a human to call back within business hours.
New setup: Voice AI at $82/month. Answers 100% of calls 24/7. Qualifies leads in real-time. Books inspection slots. Fires SMS confirmation within 60 seconds.
The contractor saves $318/month on the answering service alone. But that's not the real ROI number. The real number is the previously missed calls. If the agent converts even 2 additional jobs per month that would have been missed calls, and the average job value is $12,000, that's $24,000 in revenue from an $82/month investment. That's the conversation I have with clients when they ask if it's worth it.
How to frame this for contractor clients:
"This costs about as much as a nice dinner out each month. If it captures one job you would have otherwise missed, it pays for itself for the next two years. And it answers the phone every time, even at midnight."
Testing, Launch, and Monitoring
Before you go live, Retell has a built-in call simulator that lets you test the agent from your browser. Use it extensively — call in as a stressed homeowner, a renter, someone who wants a price, someone who's angry. The first week of calls will reveal gaps in your system prompt that the simulator doesn't catch.
Week 1: What to Tune
After 50 real calls, here are the fixes I made based on listening to recordings:
- Agent was cutting off callers → Dropped interruption_sensitivity from 0.6 to 0.4
- Address capture was incomplete → Added explicit instruction: "Repeat the address back to confirm accuracy"
- Storm date was missing from JSON → Made storm_date a required field in the prompt for storm_damage issue type
- Agent was quoting vague timelines → Added: "Never promise specific arrival times — say our scheduler will confirm the exact window"
- Callers hung up during JSON output → Moved JSON to a silent background message instead of spoken — Retell supports this via custom analysis data configuration
Key Metrics to Track
In the Retell dashboard you can see call logs, transcripts, and duration data. The metrics I watch monthly:
- Answer rate — Should be near 100%. If calls are going to voicemail, check your phone routing config.
- Lead capture rate — What % of calls produce a GHL contact. Target: 55–70% (exclude wrong numbers and renters).
- Average call duration — 3–4 minutes is healthy. Under 2 min means calls are ending before data is captured. Over 7 min means the agent is rambling.
- Webhook success rate — Monitor in n8n. A failed webhook means a lead is lost. Set up error alerting immediately.
- Inspection show rate — Track via GHL. If booked appointments aren't showing, the agent may be booking without confirming availability.
Setting Up Error Alerts
In n8n, add an Error Trigger workflow that fires when any workflow fails. Route it to Slack or SMS:
// n8n Error Trigger → Slack notification
// Error Trigger node catches any failed execution
// Code node formats the alert:
const error = $input.first().json;
return [{
json: {
text: [
"🚨 *Retell Webhook Failed*",
`Workflow: ${error.workflowName}`,
`Error: ${error.lastNodeExecuted} — ${error.errorMessage}`,
`Call ID: ${error.data?.call?.call_id || 'unknown'}`,
"Check n8n executions for full details"
].join('\n')
}
}];Handing Off to the Client
When you hand this system to a contractor client, give them a one-page doc covering: where to listen to recordings (Retell dashboard), how to see new leads (GHL pipeline), what to do if the agent says something wrong (message you, don't touch Retell config), and what the cost looks like per month.
The contractor doesn't need to understand how Retell works. They need to know that the phone is being answered, leads are showing up in GHL, and the cost is predictable. Keep the handoff doc to those three things.
The Real Unlock: Speed-to-Lead at Scale
The roofing industry has a speed-to-lead problem that voice AI solves better than any other technology available right now. An answering service calls back in hours. A live receptionist can only handle one call at a time. A voice AI agent answers the second call while still finishing the first, at 2am, with the same energy it had at 9am.
The system I built with Retell AI and Claude isn't magic — it's a well-configured voice agent with a solid system prompt, clean webhook integration, and a contractor CRM that knows what to do with the data. The technology is accessible enough that any agency owner or developer can set this up. The configuration decisions are where the work actually is.
Three months after going live with my contractor client, their inbound call answer rate is 100%. Inspection bookings from after-hours calls increased 340% compared to the answering service period. The only calls that don't convert to a GHL lead are renters and wrong numbers — and even those get handled gracefully instead of ringing through to voicemail.
If you're building this for a client, start with the system prompt. Get the conversation flows right before you tune anything else. And listen to the first 50 real calls — they'll tell you everything the simulator doesn't.
Building this for a client?
I help agencies and contractors deploy voice AI and automation systems as part of my work at VIXI LLC. If you want help setting this up or adapting it for a different vertical, reach out.
Get in touch →