Source: I Built A Voice Agent That Calls Every New Lead N8N + Vapi
A walkthrough of an n8n workflow that fires an outbound AI voice call the moment a website form is submitted, qualifies the lead conversationally, and writes the structured results back to a Google Sheet. The build uses n8n for orchestration and Vapi for the voice agent. The same pattern translates directly to a dental-practice intake flow where every new lead receives a callback within 60 seconds — before they shop a competitor.
Key Takeaways
- The end-to-end loop is six stages: form trigger → phone-number normalization (code node) → branch on validity → Vapi
POST /call→ polling loop onGET /call/:id→ Google Sheet write with structured outputs. - Vapi requires two API calls per conversation: one to create the call (returns immediately with a call ID, no transcript), one to get the call (polled until
status: ended). - A code node — written via a one-shot Claude prompt by pasting incoming JSON — strips parentheses, dashes, country codes, and emits the literal string
incorrect formatfor anything that isn’t a clean 10-digit US number, which short-circuits to a “bad number” sheet row instead of a failed Vapi request. - Dynamic prompt variables (
{{lead_name}},{{lead_company_name}},{{lead_request}}) are substituted at call time via Vapi’sassistantOverridesfield, so one Vapi assistant handles every prospect with personalized context. - Vapi’s structured outputs (which deprecated the older
summaryandsuccessEvaluationfields) define the JSON schema the agent extracts during the call — interest, motivation, urgency, past experience, budget, paid intent, status — and are linked per-assistant. - Vapi gives 10 free US numbers per account but caps daily outbound calls; production requires importing a Twilio number to scale without limits.
- Ethical practice in the demo: the agent introduces itself as “an AI agent calling from [company]” on the first turn — a baseline worth keeping for any WEO Marketly deployment.
How It Works
Trigger. A native n8n form submission node stands in for a real website webhook. Captured fields: name, phone, email, company, role, request, company size.
Normalize. A code node receives the form JSON and reformats the phone number to a clean 10-digit string. If the input has too few or too many digits or an unparseable country prefix, it outputs the literal string incorrect format.
Branch. An IF node checks phone === "incorrect format". True branch logs the bad submission to the Google Sheet and stops. False branch proceeds to the Vapi call.
Create call. An HTTP Request node POSTs to https://api.vapi.ai/call with bearer-token auth. Body fields: assistantId, phoneNumberId (the Twilio number), customer.number (prefixed with +1), and assistantOverrides.variableValues containing the dynamic prompt variables. Vapi responds with a call ID and status queued.
Poll. A wait node sleeps 60 seconds (typical conversation length), then a second HTTP Request node GETs /call/:id using the call ID. A limit node trims a Vapi response bug that occasionally returns 26 duplicate items down to the first one. An IF node checks status === "ended" — false branch waits 10 seconds and re-polls; true branch continues.
Voicemail check. A second IF node inspects endedReason. If it equals voicemail, the row is logged with a “callback needed” status. Otherwise the structured output JSON is unpacked from the deeply nested artifacts object and written to the Google Sheet with all qualification fields.
Implementation
Tool/Service: Vapi (voice agent API) + n8n (orchestration) + Twilio (phone number) + Google Sheets (CRM logging).
Setup:
- Create a Vapi account, build a “Lead Qualifier” assistant with a system prompt containing identity, style, response guidelines, prospect-information variables in
{{double_curly_braces}}, conversation flow, and required topics (interest, motivation, urgency, past experience, budget, paid intent). - In Vapi’s analysis section, define structured outputs as named string/boolean fields and link them to the assistant.
- Choose a voice (the demo uses Vapi’s “Elliot” voice) and toggle on the predefined “end call” function.
- Import a Twilio phone number into Vapi to bypass the daily outbound cap.
- In n8n, build the workflow: form trigger → code node (normalize phone) → IF (validity) → HTTP POST (create call) → wait → HTTP GET (poll) → limit → IF (status ended) → IF (voicemail) → Google Sheets append.
Cost: Not stated explicitly in the source — Vapi pricing is per-minute (combination of LLM tokens, voice synthesis, and telephony). The video does not cite a per-call dollar figure. Free tier includes 10 US numbers with a daily outbound cap; Twilio numbers required to scale. Cost data not available^[inferred].
Integration notes:
- Bearer-token auth is reusable across both HTTP nodes once saved as a generic credential in n8n.
- The
assistantOverrides.variableValuesobject is how dynamic context flows in — every prompt variable wrapped in{{}}in the system prompt must have a matching key here. - Structured outputs live deep inside
artifacts.messages[...].analysisin the GET response — drilling down through the JSON viewer is the only practical way to map fields. - Hardcoding
+1in the phone number assumes US-only leads. International deployments need either a country-code form field or a smarter normalizer. - The 60-second initial wait is tunable — shorter for low-pickup-rate scenarios, longer for verbose conversations.
Try It
- Spin up a Vapi assistant with a system prompt containing 5–7 dynamic variables and 5–7 structured-output fields aligned to the qualifying questions a dental practice asks new leads (urgency, insurance, procedure interest, preferred appointment window).
- Import the Twilio number the practice already uses for SMS — calls show the familiar caller ID instead of a random Vapi number.
- Build the n8n skeleton as a webhook trigger from the existing intake form (replace the native form node with a Webhook node); pin sample data so you can test the flow without burning real Vapi minutes.
- Wire the result row into the practice’s CRM (GoHighLevel, etc.) instead of Google Sheets so the qualified lead lands in the same pipeline as a manually-handled inquiry.