<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: olamilekanadebayo55-cmyk</title>
    <description>The latest articles on DEV Community by olamilekanadebayo55-cmyk (@olamilekanadebayo55cmyk).</description>
    <link>https://dev.to/olamilekanadebayo55cmyk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4165057%2F4d481325-0b5a-483f-93a9-73f4cb03a210.png</url>
      <title>DEV Community: olamilekanadebayo55-cmyk</title>
      <link>https://dev.to/olamilekanadebayo55cmyk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/olamilekanadebayo55cmyk"/>
    <language>en</language>
    <item>
      <title>How I Built an n8n AI Voice Outreach Workflow for Roofing Leads</title>
      <dc:creator>olamilekanadebayo55-cmyk</dc:creator>
      <pubDate>Tue, 06 Oct 2026 00:29:16 +0000</pubDate>
      <link>https://dev.to/olamilekanadebayo55cmyk/how-i-built-an-n8n-ai-voice-outreach-workflow-for-roofing-leads-36ge</link>
      <guid>https://dev.to/olamilekanadebayo55cmyk/how-i-built-an-n8n-ai-voice-outreach-workflow-for-roofing-leads-36ge</guid>
      <description>&lt;p&gt;I recently built an automation system that connects lead discovery, AI&lt;br&gt;
personalization, voice outreach, and lead tracking into a single n8n&lt;br&gt;
workflow.&lt;/p&gt;

&lt;p&gt;The project was designed around a simple problem:&lt;/p&gt;

&lt;p&gt;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?&lt;/p&gt;

&lt;p&gt;The architecture&lt;/p&gt;

&lt;p&gt;The system is split into two major workflows.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
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:

&lt;div class="ltag-netlify"&gt;
  &lt;iframe src="https://quiet-concha-127eed.netlify.app/work/roofing-lead-automation.html" title="Netlify embed"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;

&lt;div class="ltag-netlify"&gt;
  &lt;iframe src="https://quiet-concha-127eed.netlify.app/" title="Netlify embed"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;The goal of the project was not simply to connect a collection of APIs.&lt;/p&gt;

&lt;p&gt;It was to create a workflow where each component has a clear responsibility&lt;br&gt;
and the overall system can continue operating with minimal manual&lt;br&gt;
intervention.&lt;/p&gt;

&lt;p&gt;I'd be interested to hear how other n8n builders approach:&lt;/p&gt;

&lt;p&gt;AI voice workflow reliability&lt;br&gt;
retry handling&lt;br&gt;
lead-state management&lt;br&gt;
human handoff&lt;br&gt;
call outcome classification&lt;/p&gt;

&lt;p&gt;text&lt;/p&gt;
&lt;h1&gt;
  
  
  n8n
&lt;/h1&gt;
&lt;h1&gt;
  
  
  automation
&lt;/h1&gt;
&lt;h1&gt;
  
  
  ai
&lt;/h1&gt;
&lt;h1&gt;
  
  
  workflow
&lt;/h1&gt;
&lt;h1&gt;
  
  
  voicetech
&lt;/h1&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>voiceai</category>
      <category>ai</category>
      <category>automation</category>
      <category>leadgeneration</category>
    </item>
  </channel>
</rss>
