I recently built an automation system that connects lead discovery, AI
personalization, voice outreach, and lead tracking into a single n8n
workflow.
The project was designed around a simple problem:
How can a business identify relevant prospects, personalize the outreach, contact them, and keep track of the result without manually moving data between several tools?
The architecture
The system is split into two major workflows.
text
Lead Discovery
↓
Lead Filtering
↓
Google Sheets
↓
Eligible Lead Selection
↓
Local Time Check
↓
Competitor Research
↓
AI Personalization
↓
Voice Call
↓
Call Status
↓
AI Outcome Classification
↓
Google Sheets
The separation between discovery and outreach was intentional.
The lead-generation process can run independently, while the outreach
workflow consumes eligible leads from the lead database.
1. Lead discovery
The first workflow collects potential roofing businesses and applies
qualification rules before adding them to the lead database.
The general process is:
Target City
↓
Apify / Google Places
↓
Business Filtering
↓
Phone Check
↓
Duplicate Check
↓
Google Sheets
The workflow can be configured to focus on businesses that match specific
campaign criteria.
One important part is deduplication. Instead of continuously adding the same
business, the workflow compares the incoming business and phone combination
against existing records.
2. Selecting the next lead
Once leads are available, the outreach workflow selects one eligible lead.
I wanted the workflow to distinguish between:
new leads
leads that requested a follow-up
leads that should no longer be contacted
Follow-ups receive priority over completely new leads.
This creates a simple queue rather than attempting to process the entire
spreadsheet at once.
3. Local calling hours
One of the more interesting pieces of the workflow is the local-time check.
A lead's state is mapped to a time zone, and the workflow checks whether the
current local time falls inside the configured calling window.
The workflow uses a 9 AM–9 PM window in the reference implementation.
If eligible leads exist but none are currently inside their local calling
window, the workflow waits and checks again instead of simply stopping.
This is a small piece of logic, but it makes the automation much more
practical.
4. Competitor research
Before generating the opening line, the workflow performs a search for local
competitor information.
The idea is to give the AI more context than simply:
Business Name + Phone Number
Instead, the personalization step can receive information such as:
business name
city
rating
review count
competitor
competitor search information
That information is then passed to the AI generation step.
5. AI-generated opening line
OpenAI is used to generate a short personalized opening line.
The workflow asks the model to produce a single tailored sentence rather than
an entire long sales script.
This keeps the AI component focused on personalization while the rest of the
workflow handles orchestration.
6. AI voice calling
The generated opener is then passed into Vapi.
The call receives variables such as:
business_name
city
rating
review_count
competitor_name
competitor_detail
opener
This allows the voice assistant to receive context about the specific lead
rather than using exactly the same opening for every prospect.
7. Polling the call
The workflow doesn't immediately assume that the call is finished.
After starting the call, it waits and checks the call status.
The system captures information such as:
call status
ended reason
summary
transcript
polling count
This information becomes the input for the next AI step.
8. Classifying the outcome
The workflow then asks the AI to classify the call.
Possible outcomes include:
Booked
Not Interested
Follow-Up Requested
No Answer
Do Not Call
Call Timeout
The classification is then written back into the lead database.
This means the spreadsheet becomes more than a list of prospects. It becomes
the state store for the automation.
9. Why I used n8n
The main reason I chose n8n was orchestration.
Each individual service can perform one job well:
Apify → data collection
Google Sheets → lead storage
SerpAPI → search
OpenAI → reasoning/personalization
Vapi → voice interaction
n8n connects those pieces and controls when each one should run.
The interesting engineering problem isn't necessarily any individual API.
It's making the entire sequence behave predictably.
Lessons from the build
A few things stood out while building the workflow.
Start with state
Before adding more AI, define what state the lead can be in.
For example:
New
↓
Called
↓
Follow-Up Requested
↓
Booked
And make sure there are terminal states such as:
Not Interested
Do Not Call
Don't let AI control everything
AI is useful for tasks such as personalization and classification.
But deterministic workflow logic should handle things like:
timing
routing
duplicate detection
retries
data storage
call pacing
That separation makes the system easier to reason about.
Build around failure
External APIs fail.
Calls don't always connect.
A search can return no useful result.
An AI response can be malformed.
A production workflow therefore needs explicit handling for these situations
rather than assuming every request succeeds.
Final architecture
The resulting system looks like this:
┌──────────────────┐
│ Lead Discovery │
└────────┬─────────┘
↓
┌──────────────────┐
│ Google Sheets │
└────────┬─────────┘
↓
┌──────────────────┐
│ Eligible Lead │
│ Selection │
└────────┬─────────┘
↓
┌──────────────────┐
│ Local Time Check │
└────────┬─────────┘
↓
┌──────────────────┐
│ Competitor │
│ Research │
└────────┬─────────┘
↓
┌──────────────────┐
│ OpenAI Personal- │
│ ization │
└────────┬─────────┘
↓
┌──────────────────┐
│ Vapi │
│ Voice Call │
└────────┬─────────┘
↓
┌──────────────────┐
│ Outcome Analysis │
└────────┬─────────┘
↓
┌──────────────────┐
│ Google Sheets │
└──────────────────┘
I documented the roofing automation project here:
The goal of the project was not simply to connect a collection of APIs.
It was to create a workflow where each component has a clear responsibility
and the overall system can continue operating with minimal manual
intervention.
I'd be interested to hear how other n8n builders approach:
AI voice workflow reliability
retry handling
lead-state management
human handoff
call outcome classification
text
n8n
automation
ai
workflow
voicetech
Top comments (2)
For the business-and-phone duplicate check, I'd normalise every number to E.164 before comparing, since the same business can come back in national format from one source and international format from another, and a string match then counts it as two leads and two calls. The local-time check has a similar edge: Texas, Florida, Tennessee and Kentucky each span two time zones, so mapping state to zone can put a call an hour outside the window. Taking the time zone from the business's coordinates in Places avoids that, and keying the Do Not Call state on the normalised number lets it survive a re-scrape.
Nice build. For roofing, the outreach side is often less of a bottleneck than what happens when the homeowner responds. If the reply lands on a form that asks roof type, issue and urgency one question at a time, the crew can quote faster. I built chatform.in for that inbound side, and it posts answers to a webhook, so it would slot into an n8n flow like yours.