{
  "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
          }
        ]
      ]
    }
  }
}