AutomationFlowsMarketing & Ads › Qualify Inbound Leads with Vapi Voice AI and Log Results to Google Sheets

Qualify Inbound Leads with Vapi Voice AI and Log Results to Google Sheets

ByAlejandro Alfonso @alejandro02 on n8n.io

Agencies, sales teams, and service businesses who want to instantly qualify inbound leads with an AI-powered phone call — no manual follow-up needed.

Event trigger★★★★☆ complexity18 nodesForm TriggerGoogle SheetsHTTP Request
Marketing & Ads Trigger: Event Nodes: 18 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #13948 — we link there as the canonical source.

This workflow follows the Form Trigger → Google Sheets recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Qualify leads with AI voice calls using Vapi and log to Google Sheets",
  "tags": [],
  "nodes": [
    {
      "id": "ad093e33-04c3-4799-8208-725e96ed9525",
      "name": "On form submission",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        0,
        0
      ],
      "parameters": {
        "options": {},
        "formTitle": "Work with us!",
        "formFields": {
          "values": [
            {
              "fieldName": "Name",
              "fieldLabel": "Name",
              "requiredField": true
            },
            {
              "fieldName": "Phone Number",
              "fieldType": "number",
              "fieldLabel": "Phone Number",
              "requiredField": true
            },
            {
              "fieldName": "Email",
              "fieldType": "email",
              "fieldLabel": "Email",
              "requiredField": true
            },
            {
              "fieldName": "Company Name",
              "fieldLabel": "Company Name",
              "requiredField": true
            },
            {
              "fieldName": "Role",
              "fieldLabel": "Role",
              "requiredField": true
            },
            {
              "fieldName": "Request",
              "fieldLabel": "Request",
              "requiredField": true
            },
            {
              "fieldName": "Company Size",
              "fieldType": "dropdown",
              "fieldLabel": "Company Size",
              "fieldOptions": {
                "values": [
                  {
                    "option": "1"
                  },
                  {
                    "option": "2-10"
                  },
                  {
                    "option": "11-50"
                  },
                  {
                    "option": "51-100"
                  },
                  {
                    "option": "101+"
                  }
                ]
              },
              "requiredField": true
            }
          ]
        },
        "formDescription": "Fill out the form and we will reach out ASAP."
      },
      "typeVersion": 2.4
    },
    {
      "id": "9bb2b9f1-9a01-4974-a4f6-dec5a3f784f6",
      "name": "Standardize Data",
      "type": "n8n-nodes-base.code",
      "position": [
        160,
        0
      ],
      "parameters": {
        "jsCode": "// Get all input items\nconst items = $input.all();\n\n// Process each item\nreturn items.map(item => {\n  const phoneNumber = item.json['Phone Number'];\n  \n  // Convert to string and remove all non-digit characters\n  let cleanNumber = String(phoneNumber).replace(/\\D/g, '');\n  \n  // Handle different cases\n  let standardizedNumber;\n  \n  if (cleanNumber.length === 10) {\n    // Perfect - just 10 digits\n    standardizedNumber = cleanNumber;\n  } else if (cleanNumber.length === 11 && cleanNumber.startsWith('1')) {\n    // US number with country code +1\n    standardizedNumber = cleanNumber.slice(1);\n  } else {\n    // Wrong format - too many, too few, or wrong country code\n    standardizedNumber = 'incorrect format';\n  }\n  \n  // Return the item with standardized phone number\n  return {\n    json: {\n      ...item.json,\n      'Phone Number': standardizedNumber\n    }\n  };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "6dd0c0c5-36ff-4e1f-9f12-d77f4ef26a41",
      "name": "Incorrect Phone?",
      "type": "n8n-nodes-base.if",
      "position": [
        304,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "e68a68dc-720b-423e-84f5-1b7aa9238daa",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json[\"Phone Number\"] }}",
              "rightValue": "incorrect format"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "ef74d372-f563-4c31-bcb4-5cce5c8fc34b",
      "name": "Log Incorrect Phone",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        464,
        -80
      ],
      "parameters": {
        "columns": {
          "value": {
            "Date": "={{ $now.format('yyyy-MM-dd hh:mm a') }}",
            "Name": "={{ $json.Name }}",
            "Role": "={{ $json.Role }}",
            "Email": "={{ $json.Email }}",
            "Phone": "={{ $json['Phone Number'] }}",
            "Status": "Incorrect Phone #",
            "Company": "={{ $json['Company Name'] }}",
            "Request": "={{ $json.Request }}",
            "Company Size": "={{ $json['Company Size'] }}"
          },
          "schema": [],
          "mappingMode": "defineBelow",
          "matchingColumns": []
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": ""
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "bd6f9d29-d3b5-49dc-a548-ea10ede9aece",
      "name": "Call Lead",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        464,
        96
      ],
      "parameters": {
        "url": "https://api.vapi.ai/call",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"assistantId\": \"YOUR_VAPI_ASSISTANT_ID\",\n  \"phoneNumberId\": \"YOUR_VAPI_PHONE_NUMBER_ID\",\n  \"customers\": [\n    {\n      \"number\": \"+1{{ $json['Phone Number'] }}\"\n    }\n  ],\n  \"assistantOverrides\": {\n    \"variableValues\": {\n      \"lead_name\": \"{{ $json.Name }}\",\n      \"lead_company_name\": \"{{ $json['Company Name'] }}\",\n      \"lead_request\": \"{{ $json.Request }}\"\n    }\n  }\n}\n",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "typeVersion": 4.3
    },
    {
      "id": "d03a0331-d6a8-4b9c-aa07-4a1a4fcf7cd2",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        608,
        0
      ],
      "parameters": {
        "amount": 60
      },
      "typeVersion": 1.1
    },
    {
      "id": "64dfa049-c707-43e5-b6bf-8fd84266bdb2",
      "name": "Get Call Details",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        752,
        0
      ],
      "parameters": {
        "url": "=https://api.vapi.ai/call/{{ $json.results[0].id }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "executeOnce": true,
      "typeVersion": 4.3
    },
    {
      "id": "4d4f3eb0-4d8d-4cb1-8702-5ab148a2944c",
      "name": "Limit",
      "type": "n8n-nodes-base.limit",
      "position": [
        896,
        0
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "7d7d771f-9b44-46ef-8856-cccb5a6b808e",
      "name": "Ended?",
      "type": "n8n-nodes-base.if",
      "position": [
        1040,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "30c7ebf4-6de9-49f3-9a27-b7f3a7e2b558",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.status }}",
              "rightValue": "ended"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "d5d6ef3d-9969-471a-87d8-641be35068c5",
      "name": "Polling",
      "type": "n8n-nodes-base.wait",
      "position": [
        1216,
        80
      ],
      "parameters": {
        "amount": 10
      },
      "typeVersion": 1.1
    },
    {
      "id": "c7a36779-39fe-4310-8c8d-1cab38c68eb3",
      "name": "Voicemail?",
      "type": "n8n-nodes-base.if",
      "position": [
        1216,
        -80
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "73716a76-22fa-4f8c-880f-b76350b436f9",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.endedReason }}",
              "rightValue": "voicemail"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "7ca27023-51b0-41df-a5e1-25eed9343dc8",
      "name": "Log Voicemail",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1392,
        -80
      ],
      "parameters": {
        "columns": {
          "value": {
            "Date": "={{ $now.format('yyyy-MM-dd hh:mm a') }}",
            "Name": "={{ $('On form submission').item.json.Name }}",
            "Role": "={{ $('On form submission').item.json.Role }}",
            "Email": "={{ $('On form submission').item.json.Email }}",
            "Phone": "={{ $('On form submission').item.json['Phone Number'] }}",
            "Budget": "N/A",
            "Status": "Call Back",
            "Company": "={{ $('On form submission').item.json['Company Name'] }}",
            "Intent?": "N/A",
            "Request": "={{ $('On form submission').item.json.Request }}",
            "Urgency": "N/A",
            "Motivation": "N/A",
            "Company Size": "={{ $('On form submission').item.json['Company Size'] }}",
            "Past Experience": "N/A",
            "Service Interest": "N/A"
          },
          "schema": [],
          "mappingMode": "defineBelow",
          "matchingColumns": []
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": ""
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "20f7efd3-42b0-4680-97b9-502d4e12cf48",
      "name": "Log Complete",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1392,
        80
      ],
      "parameters": {
        "columns": {
          "value": {
            "Date": "={{ $now.format('yyyy-MM-dd hh:mm a') }}",
            "Name": "={{ $('On form submission').item.json.Name }}",
            "Role": "={{ $('On form submission').item.json.Role }}",
            "Email": "={{ $('On form submission').item.json.Email }}",
            "Phone": "={{ $('On form submission').item.json['Phone Number'] }}",
            "Budget": "={{ $('Limit').item.json.artifact.structuredOutputs['YOUR_BUDGET_UUID'].result }}",
            "Status": "Complete",
            "Company": "={{ $('On form submission').item.json['Company Name'] }}",
            "Intent?": "={{ $('Limit').item.json.artifact.structuredOutputs['YOUR_INTENT_UUID'].result }}",
            "Request": "={{ $('On form submission').item.json.Request }}",
            "Urgency": "={{ $('Limit').item.json.artifact.structuredOutputs['YOUR_URGENCY_UUID'].result }}",
            "Motivation": "={{ $('Limit').item.json.artifact.structuredOutputs['YOUR_MOTIVATION_UUID'].result }}",
            "Company Size": "={{ $('On form submission').item.json['Company Size'] }}",
            "Past Experience": "={{ $('Limit').item.json.artifact.structuredOutputs['YOUR_PAST_EXPERIENCE_UUID'].result }}",
            "Service Interest": "={{ $('Limit').item.json.artifact.structuredOutputs['YOUR_SERVICE_INTEREST_UUID'].result }}"
          },
          "schema": [],
          "mappingMode": "defineBelow",
          "matchingColumns": []
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": ""
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "sticky-main-new",
      "name": "Sticky Note Main",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -640,
        -576
      ],
      "parameters": {
        "width": 520,
        "height": 950,
        "content": "## Qualify Leads with AI Voice Calls Using Vapi and Log to Google Sheets\n\n### Who is this for\nAgencies, sales teams, and service businesses who want to instantly qualify inbound leads with an AI-powered phone call instead of manual follow-up.\n\n### What it does\n1. Captures lead info via a web form (name, phone, email, company, role, request)\n2. Validates the phone number format\n3. Triggers an AI voice call via Vapi to qualify the lead\n4. Waits for the call to complete, polling until finished\n5. Logs results to Google Sheets \u2014 including AI-extracted insights like service interest, motivation, urgency, budget, and intent\n\n### How to set up\n1. Create a Vapi account and set up a voice assistant with structured outputs for: Service Interest, Motivation, Urgency, Past Experience, Budget, Intent\n2. Copy the Google Sheet template or create one with these headers:\n   `Date` \u00b7 `Name` \u00b7 `Phone` \u00b7 `Email` \u00b7 `Company` \u00b7 `Role` \u00b7 `Request` \u00b7 `Company Size` \u00b7 `Service Interest` \u00b7 `Motivation` \u00b7 `Urgency` \u00b7 `Past Experience` \u00b7 `Budget` \u00b7 `Intent?` \u00b7 `Status`\n3. Update the **Call Lead** node with your Vapi assistant ID and phone number ID\n4. Update the **Log Complete** node with your Vapi structured output UUIDs\n5. Paste your Google Sheet URL into all three Google Sheets nodes\n6. Connect your Bearer Auth (Vapi API key) and Google Sheets credentials\n7. Activate the workflow and share the form URL\n\n### Requirements\n- n8n account (cloud or self-hosted)\n- Vapi account with a configured voice assistant and phone number\n- Google Sheets (one sheet with the column headers above)\n\n### How to customize\n- Edit the form fields to match your intake needs\n- Adjust the Vapi assistant prompt for your industry\n- Change the wait time (default 60s) based on typical call length\n- Add email notifications when high-intent leads are detected"
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-step1",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        -400
      ],
      "parameters": {
        "color": 6,
        "width": 400,
        "height": 232,
        "content": "## 1. Form & Phone Validation\n\nThe **On form submission** node captures lead details via a web form. The **Standardize Data** node cleans the phone input to a 10-digit US format. Invalid numbers get logged to Google Sheets and skipped.\n\n**Setup:** Customize the form fields and title to match your business."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-step2",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        400,
        -400
      ],
      "parameters": {
        "color": 6,
        "width": 400,
        "height": 232,
        "content": "## 2. AI Voice Call\n\nValid leads get an instant phone call from your Vapi AI assistant. The assistant qualifies them through a natural conversation, gathering structured data about their needs.\n\n**Setup:** Update the Vapi assistant ID and phone number ID in the **Call Lead** node JSON body."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-step3",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        700,
        -400
      ],
      "parameters": {
        "color": 6,
        "width": 400,
        "height": 232,
        "content": "## 3. Call Monitoring\n\nThe workflow waits 60 seconds for the call to complete, then polls the Vapi API every 10 seconds until the call status is \"ended\". This ensures no results are lost even for longer conversations.\n\n**Customize:** Adjust the initial wait time in **Wait** if your calls are typically shorter or longer."
      },
      "typeVersion": 1
    },
    {
      "id": "sticky-step4",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1150,
        -400
      ],
      "parameters": {
        "color": 6,
        "width": 450,
        "height": 320,
        "content": "## 4. Results Logging\n\nCompleted calls are logged with AI-extracted insights: service interest, motivation, urgency, past experience, budget, and intent. Voicemails get logged separately with a \"Call Back\" status.\n\n**Setup:** Replace the structured output UUIDs in **Log Complete** with the actual UUIDs from your Vapi assistant configuration.\n\n**Google Sheet headers:**\n`Date` \u00b7 `Name` \u00b7 `Phone` \u00b7 `Email` \u00b7 `Company` \u00b7 `Role` \u00b7 `Request` \u00b7 `Company Size` \u00b7 `Service Interest` \u00b7 `Motivation` \u00b7 `Urgency` \u00b7 `Past Experience` \u00b7 `Budget` \u00b7 `Intent?` \u00b7 `Status`"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Wait": {
      "main": [
        [
          {
            "node": "Get Call Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Limit": {
      "main": [
        [
          {
            "node": "Ended?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ended?": {
      "main": [
        [
          {
            "node": "Voicemail?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Polling",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Polling": {
      "main": [
        [
          {
            "node": "Get Call Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Call Lead": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Voicemail?": {
      "main": [
        [
          {
            "node": "Log Voicemail",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Complete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Call Details": {
      "main": [
        [
          {
            "node": "Limit",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Incorrect Phone?": {
      "main": [
        [
          {
            "node": "Log Incorrect Phone",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Call Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Standardize Data": {
      "main": [
        [
          {
            "node": "Incorrect Phone?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "On form submission": {
      "main": [
        [
          {
            "node": "Standardize Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Agencies, sales teams, and service businesses who want to instantly qualify inbound leads with an AI-powered phone call — no manual follow-up needed.

Source: https://n8n.io/workflows/13948/ — original creator credit. Request a take-down →

More Marketing & Ads workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Marketing & Ads

This repository contains an SLA-based lead routing workflow built in n8n, designed to ensure fast lead response, fair sales distribution, and controlled escalation without relying on a full CRM system

Form Trigger, Google Sheets, Slack +1
Marketing & Ads

How it works A form trigger accepts an Industry + Location query (e.g. Accountants London). Text Search Page 1 calls Google Places Text Search to return results and a nextpagetoken. Conditional checks

HTTP Request, Form Trigger, Google Sheets
Marketing & Ads

This n8n template automates lead generation by scraping Google Maps using the Olostep API. It extracts business names, locations, websites, phone numbers, and decision-maker names (CEO, Founder, etc.)

Form Trigger, HTTP Request, Google Sheets
Marketing & Ads

This workflow is a powerful B2B Lead Generation engine designed specifically for SDRs (Sales Development Representatives). It automates the entire process of finding, enriching, and qualifying prospec

Form Trigger, HTTP Request, Google Sheets
Marketing & Ads

This workflow automates the process of generating niche-specific business leads from Google Maps, leveraging the Google Places API and Google Sheets for seamless data collection and storage. Business

HTTP Request, Form Trigger, Google Sheets