{"active":false,"connections":{"Book Calendar Event":{"main":[[{"index":0,"node":"Notify Team","type":"main"}]]},"IF Qualified":{"main":[[{"index":0,"node":"Trigger Voice Call","type":"main"}],[{"index":0,"node":"Tag Nurture","type":"main"}]]},"Lead Webhook":{"main":[[{"index":0,"node":"Normalize Fields","type":"main"}]]},"Normalize Fields":{"main":[[{"index":0,"node":"Qualify Lead","type":"main"}]]},"OpenAI Chat Model":{"ai_languageModel":[[{"index":0,"node":"Qualify Lead","type":"ai_languageModel","index":0}]]},"Parse Decision":{"main":[[{"index":0,"node":"IF Qualified","type":"main"}]]},"Qualify Lead":{"main":[[{"index":0,"node":"Parse Decision","type":"main"}]]},"Trigger Voice Call":{"main":[[{"index":0,"node":"Book Calendar Event","type":"main"}]]}},"meta":{"instanceId":"","templateCredsSetupCompleted":false},"name":"AI Voice Agent for Lead Calling & Appointment Booking","nodes":[{"id":"1a2b3c4d-0000-4a5b-8c9d-000000000000","name":"Sticky Note","parameters":{"color":4,"content":"## AI Voice Agent for Lead Calling & Appointment Booking\n\n**Setup (10 min):**\n1. Create an **OpenAI** credential, select it in *OpenAI Chat Model*.\n2. Create an **HTTP Header Auth** credential with your voice provider's API key, select it in *Trigger Voice Call*, and replace the placeholder URL with your Vapi/Twilio call endpoint.\n3. For *Book Calendar Event*, create an **HTTP Header Auth** credential with a Google OAuth2 bearer token (or swap in n8n's Google Calendar node) and adjust the event time logic.\n4. Create a **Slack** credential, select it in *Notify Team*, and set your channel.\n5. Point your lead form at the *Lead Webhook* URL, then activate.\n\n**Human approval:** voice calls fire ONLY for AI-qualified leads. Before going live, review *Parse Decision* output on real leads — add a Wait node before *Trigger Voice Call* if you want a human sign-off on every call.\n\n**How it works:** Lead Webhook → Normalize Fields → AI qualifies (OpenAI) → IF qualified → voice call → calendar event → Slack alert. Unqualified leads are tagged for nurture.","height":420,"width":460},"position":[160,-80],"type":"n8n-nodes-base.stickyNote","typeVersion":1},{"id":"1a2b3c4d-0001-4a5b-8c9d-111111111111","name":"Lead Webhook","parameters":{"httpMethod":"POST","options":{},"path":"lead-intake"},"position":[240,300],"type":"n8n-nodes-base.webhook","typeVersion":2,"webhookId":"cortexflow-lead-intake"},{"id":"1a2b3c4d-0002-4a5b-8c9d-222222222222","name":"Normalize Fields","parameters":{"assignments":{"assignments":[{"id":"1a2b3c4d-a1","name":"name","type":"string","value":"={{ ($json.name || '').toString().trim() }}"},{"id":"1a2b3c4d-a2","name":"phone","type":"string","value":"={{ ($json.phone || '').toString().replace(/[^+\\d]/g, '') }}"},{"id":"1a2b3c4d-a3","name":"notes","type":"string","value":"={{ ($json.notes || '').toString().trim() }}"}]},"options":{}},"position":[480,300],"type":"n8n-nodes-base.set","typeVersion":3.4},{"id":"1a2b3c4d-0003-4a5b-8c9d-333333333333","name":"Qualify Lead","parameters":{"options":{"systemMessage":"You are a lead-qualification assistant. Given a lead's name, phone and notes, decide whether they are worth an immediate AI voice call. Reply with ONLY a JSON object: {\"qualified\": true/false, \"reason\": \"one short sentence\"}. Mark qualified=true only if the notes show real intent (asking for a demo, pricing, a callback, or a specific need). Be strict: vague, spammy or empty notes are NOT qualified."},"promptType":"define","text":"={{ 'Name: ' + $json.name + '\\nPhone: ' + $json.phone + '\\nNotes: ' + $json.notes }}"},"position":[720,300],"type":"@n8n/n8n-nodes-langchain.agent","typeVersion":2.2},{"credentials":{"openAiApi":{"id":"","name":"OpenAI API"}},"id":"1a2b3c4d-0004-4a5b-8c9d-444444444444","name":"OpenAI Chat Model","parameters":{"model":{"__rl":true,"cachedResultName":"gpt-4o-mini","mode":"list","value":"gpt-4o-mini"},"options":{}},"position":[720,500],"type":"@n8n/n8n-nodes-langchain.lmChatOpenAi","typeVersion":1.2},{"id":"1a2b3c4d-0005-4a5b-8c9d-555555555555","name":"Parse Decision","parameters":{"jsCode":"const lead = $('Normalize Fields').first().json;\nlet parsed = {};\ntry {\n  const raw = $input.first().json.output || '';\n  const match = raw.match(/\\{[\\s\\S]*\\}/);\n  parsed = JSON.parse(match ? match[0] : raw);\n} catch (e) { parsed = {}; }\nreturn [{ json: {\n  name: lead.name || '',\n  phone: lead.phone || '',\n  notes: lead.notes || '',\n  qualified: parsed.qualified === true,\n  reason: typeof parsed.reason === 'string' ? parsed.reason : 'No reason returned'\n}}];","mode":"runOnceForAllItems"},"position":[960,300],"type":"n8n-nodes-base.code","typeVersion":2},{"id":"1a2b3c4d-0006-4a5b-8c9d-666666666666","name":"IF Qualified","parameters":{"conditions":{"combinator":"and","conditions":[{"id":"1a2b3c4d-c1","leftValue":"={{ $json.qualified }}","operator":{"singleValue":true,"type":"boolean"},"rightValue":true}],"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict","version":2}},"looseTypeValidation":true,"options":{}},"position":[1200,300],"type":"n8n-nodes-base.if","typeVersion":2.2},{"credentials":{"httpHeaderAuth":{"id":"","name":"Voice Provider API Key"}},"id":"1a2b3c4d-0007-4a5b-8c9d-777777777777","name":"Trigger Voice Call","parameters":{"authentication":"predefinedCredentialType","jsonBody":"={\n  \"to\": \"{{ $json.phone }}\",\n  \"lead_name\": \"{{ $json.name }}\",\n  \"context\": \"{{ $json.notes }}\",\n  \"qualification_reason\": \"{{ $json.reason }}\"\n}","method":"POST","nodeCredentialType":"httpHeaderAuth","options":{},"sendBody":true,"specifyBody":"json","url":"https://api.your-voice-provider.com/v1/calls"},"position":[1440,300],"type":"n8n-nodes-base.httpRequest","typeVersion":4.2},{"credentials":{"httpHeaderAuth":{"id":"","name":"Google Calendar Bearer"}},"id":"1a2b3c4d-0008-4a5b-8c9d-888888888888","name":"Book Calendar Event","parameters":{"authentication":"predefinedCredentialType","jsonBody":"={\n  \"summary\": \"AI call follow-up: {{ $json.name }}\",\n  \"description\": \"Lead qualified by AI voice agent.\\nReason: {{ $json.reason }}\\nPhone: {{ $json.phone }}\\nNotes: {{ $json.notes }}\",\n  \"start\": { \"dateTime\": \"{{ $now.plus({days: 1}).set({hour: 10, minute: 0, second: 0}).toISO() }}\", \"timeZone\": \"UTC\" },\n  \"end\": { \"dateTime\": \"{{ $now.plus({days: 1}).set({hour: 10, minute: 30, second: 0}).toISO() }}\", \"timeZone\": \"UTC\" }\n}","method":"POST","nodeCredentialType":"httpHeaderAuth","options":{},"sendBody":true,"specifyBody":"json","url":"https://www.googleapis.com/calendar/v3/calendars/primary/events"},"position":[1680,300],"type":"n8n-nodes-base.httpRequest","typeVersion":4.2},{"credentials":{"slackOAuth2Api":{"id":"","name":"Slack API"}},"id":"1a2b3c4d-0009-4a5b-8c9d-999999999999","name":"Notify Team","parameters":{"channelId":{"__rl":true,"mode":"list","value":"C0123456789"},"operation":"post","otherOptions":{},"resource":"message","select":"channel","text":"=Voice call triggered for *{{ $json.name }}* ({{ $json.phone }}). Calendar invite created. Qualification: {{ $json.reason }}"},"position":[1920,300],"type":"n8n-nodes-base.slack","typeVersion":2.3},{"id":"1a2b3c4d-0010-4a5b-8c9d-a0a0a0a0a0a0","name":"Tag Nurture","parameters":{"assignments":{"assignments":[{"id":"1a2b3c4d-b1","name":"name","type":"string","value":"={{ $json.name }}"},{"id":"1a2b3c4d-b2","name":"phone","type":"string","value":"={{ $json.phone }}"},{"id":"1a2b3c4d-b3","name":"tag","type":"string","value":"nurture"},{"id":"1a2b3c4d-b4","name":"reason","type":"string","value":"={{ $json.reason }}"}]},"options":{}},"position":[1440,460],"type":"n8n-nodes-base.set","typeVersion":3.4}],"pinData":{},"settings":{"executionOrder":"v1"},"tags":[{"name":"cortexflow-template"}],"versionId":"cortexflow-ai-voice-agent-lead-calling-v1"}