This workflow corresponds to n8n.io template #15779 — we link there as the canonical source.
This workflow follows the Airtable → HTTP Request 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 →
{
"id": "v8zOj5dLpd1qdwTh",
"name": "Lead intake, qualification and follow-up system",
"tags": [],
"nodes": [
{
"id": "6d51b2a6-28ab-416b-b489-dfbccb3a002c",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"position": [
960,
640
],
"parameters": {
"path": "=LEAD_INTAKE_WEBHOOK_ID",
"options": {
"allowedOrigins": "*",
"responseHeaders": {
"entries": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "3288c234-0c1b-42fe-8025-8d38f2858963",
"name": "Validate & Clean Data",
"type": "n8n-nodes-base.code",
"position": [
1184,
640
],
"parameters": {
"jsCode": "// Fixed Tally webhook validation using actual structure\nconst webhookData = $input.first().json;\n\nconsole.log(\"=== PROCESSING TALLY WEBHOOK ===\");\n\nlet leadData = {};\n\ntry {\n // Access fields from the correct location: body.data.fields\n const fields = webhookData.body?.data?.fields;\n \n if (!fields || !Array.isArray(fields)) {\n throw new Error(\"No fields array found in webhook.body.data.fields\");\n }\n \n console.log(\"Processing\", fields.length, \"fields from Tally\");\n \n // Process each field\n fields.forEach((field, index) => {\n console.log(`Field ${index}: \"${field.label}\" (${field.type})`);\n \n switch(field.label) {\n case 'Name':\n leadData.name = field.value;\n console.log(`\u2713 Name: ${field.value}`);\n break;\n \n case 'Email':\n leadData.email = field.value;\n console.log(`\u2713 Email: ${field.value}`);\n break;\n \n case 'Phone Number':\n leadData.phone = field.value;\n console.log(`\u2713 Phone: ${field.value}`);\n break;\n \n case 'Company Name':\n leadData.company = field.value;\n console.log(`\u2713 Company: ${field.value}`);\n break;\n \n case 'Message':\n leadData.message = field.value;\n console.log(`\u2713 Message: ${field.value}`);\n break;\n \n case 'Company Size':\n // For dropdown fields, map the selected ID to text using options\n if (field.type === 'DROPDOWN' && field.options && field.value) {\n const selectedId = Array.isArray(field.value) ? field.value[0] : field.value;\n const selectedOption = field.options.find(opt => opt.id === selectedId);\n leadData.company_size = selectedOption ? selectedOption.text : 'Not specified';\n console.log(`\u2713 Company Size: ${selectedId} \u2192 ${leadData.company_size}`);\n } else {\n leadData.company_size = field.value || 'Not specified';\n }\n break;\n \n case 'When do you need a solution?':\n if (field.type === 'DROPDOWN' && field.options && field.value) {\n const selectedId = Array.isArray(field.value) ? field.value[0] : field.value;\n const selectedOption = field.options.find(opt => opt.id === selectedId);\n leadData.timeline = selectedOption ? selectedOption.text : 'Not specified';\n console.log(`\u2713 Timeline: ${selectedId} \u2192 ${leadData.timeline}`);\n } else {\n leadData.timeline = field.value || 'Not specified';\n }\n break;\n \n case \"What's your role in the company?\":\n if (field.type === 'DROPDOWN' && field.options && field.value) {\n const selectedId = Array.isArray(field.value) ? field.value[0] : field.value;\n const selectedOption = field.options.find(opt => opt.id === selectedId);\n leadData.role = selectedOption ? selectedOption.text : 'Not specified';\n console.log(`\u2713 Role: ${selectedId} \u2192 ${leadData.role}`);\n } else {\n leadData.role = field.value || 'Not specified';\n }\n break;\n \n default:\n console.log(`\u26a0\ufe0f Unmapped field: \"${field.label}\"`);\n }\n });\n \n console.log(\"Final extracted data:\", leadData);\n \n} catch (error) {\n console.log(\"\u274c ERROR:\", error.message);\n return [{\n json: {\n status: \"error\",\n errors: [`Processing failed: ${error.message}`],\n debug: {\n webhookStructure: typeof webhookData,\n hasBody: !!webhookData.body,\n hasBodyData: !!(webhookData.body && webhookData.body.data),\n hasFields: !!(webhookData.body && webhookData.body.data && webhookData.body.data.fields)\n }\n }\n }];\n}\n\n// Validation\nconst errors = [];\nif (!leadData.name || leadData.name.trim() === '') {\n errors.push(\"Name is required\");\n}\nif (!leadData.email || !leadData.email.includes('@')) {\n errors.push(\"Valid email is required\");\n}\nif (!leadData.phone || leadData.phone.trim() === '') {\n errors.push(\"Phone is required\");\n}\n\nif (errors.length > 0) {\n console.log(\"\u274c Validation failed:\", errors);\n return [{\n json: {\n status: \"error\",\n errors: errors,\n debug: { extractedData: leadData }\n }\n }];\n}\n\n// Create final structured lead object\nconst cleanedData = {\n name: leadData.name.trim(),\n email: leadData.email.toLowerCase().trim(),\n phone: leadData.phone.replace(/[^\\d+]/g, ''),\n company: leadData.company?.trim() || \"\",\n message: leadData.message?.trim() || \"\",\n company_size: leadData.company_size,\n timeline: leadData.timeline,\n role: leadData.role,\n timestamp: new Date().toISOString(),\n leadId: `lead_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`\n};\n\nconsole.log(\"\u2705 SUCCESS - Final lead data:\");\nconsole.log(` Company Size: ${cleanedData.company_size}`);\nconsole.log(` Timeline: ${cleanedData.timeline}`);\nconsole.log(` Role: ${cleanedData.role}`);\n\nreturn [{\n json: {\n status: \"success\",\n lead: cleanedData\n }\n}];"
},
"typeVersion": 2
},
{
"id": "31df8d4b-9c41-4d90-8734-bb12512539a4",
"name": "Check Validation Status",
"type": "n8n-nodes-base.if",
"position": [
1760,
640
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "9b310b34-90c0-4f6c-84c5-c28413168ef5",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $('Validate & Clean Data').item.json.status }}",
"rightValue": "success"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "10d016e3-2314-4c8c-a839-9dde3d2e6d84",
"name": "Create a record",
"type": "n8n-nodes-base.airtable",
"position": [
1984,
544
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appXXXXXXXXXXXXXX",
"cachedResultUrl": "https://airtable.com/appXXXXXXXXXXXXXX",
"cachedResultName": "Your Lead Base"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblXXXXXXXXXXXXXX",
"cachedResultUrl": "https://airtable.com/appXXXXXXXXXXXXXX/tblXXXXXXXXXXXXXX",
"cachedResultName": "Leads"
},
"columns": {
"value": {
"Name": "={{ $('Validate & Clean Data').item.json.lead.name }}",
"Email": "={{ $('Validate & Clean Data').item.json.lead.email }}",
"Phone": "={{ $('Validate & Clean Data').item.json.lead.phone }}",
"Status": "New",
"Company": "={{ $('Validate & Clean Data').item.json.lead.company }}",
"Message": "={{ $('Validate & Clean Data').item.json.lead.message }}",
"Lead Score": "={{ $('AI Lead Qualification').item.json.message.content.score }}",
"Company Size": "={{ $('Validate & Clean Data').item.json.lead.company_size }}",
"When do you need a solution?": "={{ $('Validate & Clean Data').item.json.lead.timeline }}",
"What's your role in the company?": "={{ $('Validate & Clean Data').item.json.lead.role }}"
},
"schema": [
{
"id": "Name",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Phone",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Phone",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Company",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Company",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Company Size",
"type": "options",
"display": true,
"options": [
{
"name": "1-10 people",
"value": "1-10 people"
},
{
"name": "11-50 people",
"value": "11-50 people"
},
{
"name": "51-200 people",
"value": "51-200 people"
},
{
"name": "201+ people",
"value": "201+ people"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Company Size",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "When do you need a solution?",
"type": "options",
"display": true,
"options": [
{
"name": "Immediately (within 2 weeks)",
"value": "Immediately (within 2 weeks)"
},
{
"name": "Soon (1-3 months)",
"value": "Soon (1-3 months)"
},
{
"name": "This year (3-12 months)",
"value": "This year (3-12 months)"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "When do you need a solution?",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "What's your role in the company?",
"type": "options",
"display": true,
"options": [
{
"name": "Owner/CEO/Founder",
"value": "Owner/CEO/Founder"
},
{
"name": "Manager/Director",
"value": "Manager/Director"
},
{
"name": "Employee/Team Member",
"value": "Employee/Team Member"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "What's your role in the company?",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Message",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Message",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Lead Score",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Lead Score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Status",
"type": "options",
"display": true,
"options": [
{
"name": "New",
"value": "New"
},
{
"name": "Contacted",
"value": "Contacted"
},
{
"name": "Qualified",
"value": "Qualified"
},
{
"name": "Unqualified",
"value": "Unqualified"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Meeting Scheduled",
"type": "options",
"display": true,
"options": [
{
"name": "Yes",
"value": "Yes"
},
{
"name": "No",
"value": "No"
},
{
"name": "Pending",
"value": "Pending"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Meeting Scheduled",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Meeting Date",
"type": "dateTime",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Meeting Date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Notes",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Notes",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Created",
"type": "string",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "Created",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Lead ID",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Lead ID",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Record ID",
"type": "string",
"display": true,
"removed": false,
"readOnly": true,
"required": false,
"displayName": "Record ID",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "create"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "00266787-e974-4f60-a81a-d762a959fc13",
"name": "Respond to Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1984,
736
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\n \"success\": false,\n \"message\": \"Validation failed\",\n \"errors\": \"Invalid data provided\"\n}"
},
"typeVersion": 1.4
},
{
"id": "d71fa900-789d-40b3-b3fa-f8fc752ee2ed",
"name": "AI Lead Qualification",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
1408,
640
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-3.5-turbo",
"cachedResultName": "GPT-3.5-TURBO"
},
"options": {},
"messages": {
"values": [
{
"role": "system",
"content": "=You are an expert B2B lead qualification specialist for AI automation services. You now have structured qualification data that is MORE RELIABLE than message analysis. Score leads 1-10 using this weighted scoring system:\n\n**PRIMARY SCORING FACTORS (70% of score):**\n\n**COMPANY SIZE SCORING:**\n- \"201+ people\" = 4 points (Enterprise - high budget, complex processes)\n- \"51-200 people\" = 3 points (Mid-market - growth stage, automation needs)\n- \"11-50 people\" = 2 points (SMB - emerging automation needs)\n- \"1-10 people\" = 1 point (Startup/small - limited budget)\n- Not specified = 0 points\n\n**TIMELINE SCORING:**\n- \"Immediately (within 2 weeks)\" = 3 points (Hot lead - urgent need)\n- \"Soon (1-3 months)\" = 2 points (Warm lead - active buying process)\n- \"This year (3-12 months)\" = 1 point (Planning phase)\n- \"Just exploring\" or not specified = 0 points\n\n**ROLE SCORING:**\n- \"Owner/CEO/Founder\" = 2 points (Decision maker)\n- \"Manager/Director\" = 1.5 points (Strong influence)\n- \"Employee/Team Member\" = 0.5 points (Limited authority)\n- Not specified = 0 points\n\n**SECONDARY FACTORS (30% of score):**\n- Professional email domain (+0.5 points)\n- Complete contact info (+0.5 points)\n- Detailed message with specific pain points (+1 point)\n- Business email vs personal (-0.5 for gmail/yahoo unless startup)\n\n**TOTAL SCORING SYSTEM:**\n- 8.5-10 points = SCORE 9-10 (HOT - Immediate call)\n- 6.5-8.4 points = SCORE 7-8 (WARM - Priority email + call within 24h)\n- 4.5-6.4 points = SCORE 5-6 (LUKEWARM - Nurture sequence)\n- Below 4.5 points = SCORE 1-4 (COLD - Minimal follow-up)\n\n**EXAMPLES:**\n- 201+ people + Immediately + Owner/CEO = 9 points = HOT LEAD\n- 51-200 people + Soon + Manager + good message = 7.5 points = WARM LEAD\n- 11-50 people + This year + Employee = 4 points = LUKEWARM\n- 1-10 people + Just exploring = 2 points = COLD\n\nIMPORTANT: Even if someone writes a brief message like 'Tell me more', if they selected '201+ people' + 'Immediately' + 'Owner/CEO', that's a 9-point HOT LEAD. The structured data is more reliable than message analysis.\n\nReturn JSON: {\"score\": X, \"priority\": \"hot/warm/lukewarm/cold\", \"reasoning\": \"specific point breakdown\", \"next_action\": \"immediate_call/priority_email/nurture_sequence/minimal_followup\", \"deal_size_estimate\": \"enterprise/mid-market/small/micro\"}"
},
{
"content": "=LEAD QUALIFICATION ANALYSIS\n\n**STRUCTURED DATA:**\n- Company Size: {{ $node['Validate & Clean Data'].json.lead.company_size }}\n- Timeline: {{ $node['Validate & Clean Data'].json.lead.timeline }}\n- Role: {{ $node['Validate & Clean Data'].json.lead.role }}\n\n**CONTACT DETAILS:**\n- Name: {{ $node['Validate & Clean Data'].json.lead.name }}\n- Email: {{ $node['Validate & Clean Data'].json.lead.email }}\n- Company: {{ $node['Validate & Clean Data'].json.lead.company }}\n- Phone: {{ $node['Validate & Clean Data'].json.lead.phone }}\n\n**MESSAGE:**\n{{ $node['Validate & Clean Data'].json.lead.message }}\n\n**SCORING INSTRUCTIONS:**\nUse the point system above. Calculate total points from company size + timeline + role + secondary factors, then map to 1-10 score. Provide detailed reasoning showing your point calculations."
}
]
},
"jsonOutput": true
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.8
},
{
"id": "1c325fd0-be68-4a0c-b4dd-df0cdb4c9fa7",
"name": "Check for Follow-Up Sequence",
"type": "n8n-nodes-base.if",
"position": [
3248,
640
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "70420fe4-f926-4afd-9d95-bbaa859f2ff5",
"operator": {
"type": "number",
"operation": "lte"
},
"leftValue": "={{ $('AI Lead Qualification').item.json.message.content.score }}",
"rightValue": 6
}
]
}
},
"typeVersion": 2.2
},
{
"id": "6f7725c3-6fe5-41f8-b60a-29a7e465da4a",
"name": "Send Nurture Email",
"type": "n8n-nodes-base.sendGrid",
"position": [
3472,
640
],
"parameters": {
"subject": "={{ $node[\"Validate & Clean Data\"].json.lead.name }}, here's how AI automation can help {{ $node[\"Validate & Clean Data\"].json.lead.company }}",
"toEmail": "={{ $node[\"Validate & Clean Data\"].json.lead.email }}",
"fromName": "Your Name - AI Automation",
"resource": "mail",
"fromEmail": "user@example.com",
"contentType": "text/html",
"contentValue": "=<p>Hi {{ $node[\"Validate & Clean Data\"].json.lead.name }},</p> <p>Thanks for your interest in AI automation! I wanted to share some insights that might help {{ $node[\"Validate & Clean Data\"].json.lead.company }}.</p> <p><strong>Companies like yours typically see results in:</strong></p> <ul> <li>40-60% reduction in manual data processing</li> <li>Automated lead qualification and routing</li> <li>Improved customer response times</li> <li>Enhanced data accuracy and insights</li> </ul> <p><strong>Free Resources:</strong></p> <p>I've prepared a case study showing how a similar company automated their lead process and increased conversion by 35%.</p> <p>Would you like me to send it along or would you like a brief call to discuss your specific automation opportunities?</p> <p>Best regards,<br>Abi</p>",
"additionalFields": {}
},
"credentials": {
"sendGridApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "48cf115e-b84a-4b36-97fe-2235045e3c4e",
"name": "Check for AI Calling",
"type": "n8n-nodes-base.if",
"position": [
2208,
544
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "a468a91b-7678-4609-8546-6ec3741b5728",
"operator": {
"type": "number",
"operation": "gte"
},
"leftValue": "={{ $('AI Lead Qualification').item.json.message.content.score }}",
"rightValue": 8
}
]
}
},
"typeVersion": 2.2
},
{
"id": "9aafc4e1-88d5-4d2e-807b-e90fc8fcab2b",
"name": "Trigger AI Phone Call",
"type": "n8n-nodes-base.httpRequest",
"position": [
3248,
448
],
"parameters": {
"url": "https://api.bland.ai/v1/calls",
"method": "POST",
"options": {},
"jsonBody": "= {\n \"phone_number\": \"{{ $node['Validate & Clean Data'].json.lead.phone }}\",\n \"task\": \"You are Sarah, an AI automation consultant. You are calling {{ $node['Validate & Clean Data'].json.lead.name }} from {{ $node['Validate & Clean Data'].json.lead.company }}. Start by saying: 'Hi, is this {{ $node['Validate & Clean Data'].json.lead.name }}?' Wait for confirmation. Then introduce yourself: 'Hi {{ $node['Validate & Clean Data'].json.lead.name }}, this is Sarah from [YOUR_COMPANY]. I'm calling about the AI automation inquiry you submitted for {{ $node['Validate & Clean Data'].json.lead.company }}. Do you have a quick minute to chat?' If they're available, ask: 'What prompted you to look into AI automation for your business?' Listen to their response, then ask about their current challenges. If they show genuine interest in learning more, say: 'I'd love to schedule a brief 15-minute consultation to discuss how we could help {{ $node['Validate & Clean Data'].json.lead.company }} specifically. {{ $node['Code in JavaScript'].json.availabilityDescription }}. What day and time would work best for you?' Once they give a preferred time, repeat it back: 'Perfect, so that's [repeat their preferred day and time]. I'll have someone send you the calendar invite for [repeat exact day/time]. Thanks so much for your time today!'\",\n \"voice\": \"maya\",\n \"max_duration\": 300,\n \"webhook\": \"https://YOUR-N8N-INSTANCE.com/webhook/CALL_OUTCOME_WEBHOOK_ID\",\n \"metadata\": {\n\"record_id\": \"{{ $node['Create a record'].json.id }}\",\n \"lead_name\": \"{{ $node['Validate & Clean Data'].json.lead.name }}\",\n \"lead_email\": \"{{ $node['Validate & Clean Data'].json.lead.email }}\",\n \"lead_id\": \"{{ $node['Validate & Clean Data'].json.lead.leadId }}\",\n \"lead_company\": \"{{ $node['Validate & Clean Data'].json.lead.company }}\",\n\"message\": \"{{ $node['Validate & Clean Data'].json.lead.message }}\"\n }\n }\n",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer YOUR_TOKEN_HERE"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "d508fb3f-5407-46d2-b402-f48207349a09",
"name": "Send Priority Email",
"type": "n8n-nodes-base.sendGrid",
"position": [
2432,
640
],
"parameters": {
"subject": "=Hi {{ $node[\"Validate & Clean Data\"].json.lead.name }}, let's discuss your AI automation needs",
"toEmail": "={{ $('Validate & Clean Data').item.json.lead.email }}",
"fromName": "Your Name - AI Automation",
"resource": "mail",
"fromEmail": "user@example.com",
"contentType": "text/html",
"contentValue": "=<p>Hi {{ $node[\"Validate & Clean Data\"].json.lead.name }},</p>\n\n<p>Thanks for your interest in AI automation! Based on your message, I can see {{ $node[\"Validate & Clean Data\"].json.lead.company }} has strong automation potential.</p>\n\n<p> I wanted to reach out personally and I'd love to schedule a 15-minute call to discuss:</p>\n<ul>\n<li>Your specific automation challenges</li>\n<li>How AI can streamline your processes</li>\n<li>ROI potential for your business</li>\n</ul>\n\n<p>When would be a good time this week?</p>\n\n<p>Best regards,<br>\nAbi<br>\nAI Automation Specialist</p>",
"additionalFields": {}
},
"credentials": {
"sendGridApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "4b33e334-7e42-4c99-9026-899e8125f0f9",
"name": "Call Outcome Webhook",
"type": "n8n-nodes-base.webhook",
"position": [
960,
1408
],
"parameters": {
"path": "CALL_OUTCOME_WEBHOOK_ID",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "40962417-a8de-49f4-bee7-29c33eb7a266",
"name": "Get availability in a calendar",
"type": "n8n-nodes-base.googleCalendar",
"position": [
2432,
448
],
"parameters": {
"options": {},
"timeMax": "={{ $now.plus(7, 'day') }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "user@example.com",
"cachedResultName": "user@example.com"
},
"resource": "calendar"
},
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "843172bc-73f7-451d-ba31-796f7c7d99da",
"name": "Code in JavaScript",
"type": "n8n-nodes-base.code",
"position": [
3024,
448
],
"parameters": {
"jsCode": "// Get calendar events - handle different data structures\nlet events = [];\nconst inputData = $input.all()[0];\n\n// Handle different possible data structures from Google Calendar\nif (inputData && inputData.json) {\n if (Array.isArray(inputData.json)) {\n events = inputData.json;\n } else if (inputData.json.items && Array.isArray(inputData.json.items)) {\n events = inputData.json.items;\n } else if (inputData.json.events && Array.isArray(inputData.json.events)) {\n events = inputData.json.events;\n }\n}\n\nconsole.log(\"Events data:\", events);\n\nconst now = new Date();\n\n// Your working hours\nconst workingHours = {\n start: 9, // 9 AM\n end: 17, // 5 PM\n days: [1, 2, 3, 4, 5], // Monday-Friday\n duration: 15 // 15-minute meetings\n};\n\n// Find all available days and general time ranges\nconst availableDays = [];\nconst specificSlots = [];\n\nfor (let day = 1; day <= 7; day++) {\n const checkDate = new Date(now.getTime() + day * 24 * 60 * 60 * 1000);\n \n if (workingHours.days.includes(checkDate.getDay())) {\n let dayHasAvailability = false;\n let availableHours = [];\n \n for (let hour = workingHours.start; hour < workingHours.end; hour++) {\n const slotTime = new Date(checkDate);\n slotTime.setHours(hour, 0, 0, 0);\n \n // Check if slot conflicts with existing events\n const hasConflict = events.length > 0 && events.some(event => {\n if (!event.start || !event.end) return false;\n const eventStart = new Date(event.start.dateTime || event.start.date);\n const eventEnd = new Date(event.end.dateTime || event.end.date);\n return slotTime >= eventStart && slotTime < eventEnd;\n });\n \n if (!hasConflict && slotTime > now) {\n dayHasAvailability = true;\n availableHours.push(hour);\n \n // Keep some specific slots for calendar booking later\n if (specificSlots.length < 5) {\n specificSlots.push(slotTime.toISOString());\n }\n }\n }\n \n if (dayHasAvailability) {\n const dayName = checkDate.toLocaleDateString('en-US', { weekday: 'long' });\n const formattedDate = checkDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });\n \n // Create time ranges instead of specific times\n const timeRanges = [];\n if (availableHours.includes(9) || availableHours.includes(10) || availableHours.includes(11)) {\n timeRanges.push('morning (9am-12pm)');\n }\n if (availableHours.includes(13) || availableHours.includes(14) || availableHours.includes(15)) {\n timeRanges.push('afternoon (1pm-4pm)');\n }\n if (availableHours.includes(16)) {\n timeRanges.push('late afternoon (4pm-5pm)');\n }\n \n if (timeRanges.length > 0) {\n const dayDescription = dayName + ' ' + formattedDate + ' - ' + timeRanges.join(' or ');\n availableDays.push(dayDescription);\n }\n }\n }\n \n if (availableDays.length >= 4) break; // Limit to 4 days of availability\n}\n\n// Create flexible availability description\nconst availabilityDescription = availableDays.length > 0 \n ? 'I have good availability: ' + availableDays.join(', ')\n : 'I have flexibility this week between 9am-5pm Monday through Friday';\n\nreturn [{ \n json: {\n availabilityDescription: availabilityDescription, // For AI script\n availableDateTime: specificSlots[0], // First specific slot for calendar booking\n allAvailableSlots: specificSlots, // All specific slots available\n debug: { totalEvents: events.length, availableDays: availableDays.length }\n }\n}];"
},
"typeVersion": 2
},
{
"id": "71f8e9d7-72f7-4570-9058-8071839b89ab",
"name": "Parse Call Outcome",
"type": "n8n-nodes-base.code",
"position": [
1184,
1312
],
"parameters": {
"jsCode": "/**\n * Parse Bland.ai webhook \u2192 normalize call outcome + surface Airtable record id\n * Works whether Bland posts `variables.metadata` or `metadata` at root.\n */\n\n//// 1) Load payload robustly\nconst fromWebhook = $items('Call Outcome Webhook', 0, 0)?.json;\nconst webhookData = (fromWebhook && Object.keys(fromWebhook).length)\n ? fromWebhook\n : ($input.first()?.json ?? {});\n\nif (!webhookData || Object.keys(webhookData).length === 0) {\n console.log('Parse Call Outcome: empty payload (no webhook item, no input). Skipping.');\n return $input.all().length ? $input.all() : [{ json: { skipped: true } }];\n}\n\nconst body = webhookData.body ?? webhookData;\n\n//// 2) Transcript (array or single field)\nlet transcriptText = '';\nif (Array.isArray(body.transcripts)) {\n transcriptText = body.transcripts\n .map(t => (t && t.text ? String(t.text) : ''))\n .filter(Boolean)\n .join(' ');\n} else if (body.transcript) {\n transcriptText = String(body.transcript);\n} else if (body.summary) {\n transcriptText = String(body.summary);\n}\n\n//// 3) Duration & disposition\nconst disposition = String(body.disposition_tag ?? '').toUpperCase();\nconst durationSec = Number(\n body.corrected_duration ??\n body.duration ??\n body.call_length ??\n webhookData.corrected_duration ??\n webhookData.duration ??\n webhookData.call_length ??\n 0\n);\n\n// \u201cConnected\u201d = actual human exchange: >5s OR any transcript captured\nconst connected = (durationSec > 5) || (transcriptText.trim().length > 0);\n\n//// 4) Lead basics\nconst vars = body.variables ?? {};\nconst meta = vars.metadata ?? body.metadata ?? {};\nconst leadData = {\n name: body.lead_name ?? meta.lead_name ?? webhookData.lead_name ?? 'Lead',\n email: body.lead_email ?? meta.lead_email ?? webhookData.lead_email ?? '',\n leadId: body.lead_id ?? meta.lead_id ?? webhookData.lead_id ?? '',\n company: body.lead_company ?? meta.lead_company ?? body.company ?? webhookData.lead_company ?? ''\n};\n\n// >>> 5) **Airtable Record ID** from metadata (this is what you\u2019ll match on later)\nconst recordId = meta.record_id || webhookData.record_id || '';\n\n//// 6) Interest heuristic\nconst tLower = transcriptText.toLowerCase();\nlet interested =\n ['yes','sure','schedule','book','sounds good',\"let's do\",'lets do','ok','okay','that works']\n .some(k => tLower.includes(k));\n\nif (!interested && ['CALL_BACK_SCHEDULED','MEETING_SCHEDULED','FOLLOW_UP'].includes(disposition)) {\n interested = true;\n}\n\n//// 7) Extract an agreed time phrase (very light NLP)\nlet agreedTime = null;\nlet agreedDateTime = null;\n\nconst ampm = '(?:am|a\\\\.m\\\\.|pm|p\\\\.m\\\\.)';\nconst patterns = [\n // friday at 11am\n new RegExp(`(?:monday|tuesday|wednesday|thursday|friday|saturday|sunday)\\\\s+at\\\\s+(\\\\d{1,2}(?::\\\\d{2})?\\\\s*${ampm})`, 'gi'),\n // 11am on friday\n new RegExp(`(\\\\d{1,2}(?::\\\\d{2})?\\\\s*${ampm})\\\\s+(?:on\\\\s+)?(?:monday|tuesday|wednesday|thursday|friday|saturday|sunday)`, 'gi'),\n // tomorrow at 3pm / next week at 2pm\n new RegExp(`(?:tomorrow|next\\\\s+\\\\w+)\\\\s+at\\\\s+(\\\\d{1,2}(?::\\\\d{2})?\\\\s*${ampm})`, 'gi'),\n // friday at eleven a.m.\n new RegExp(`(?:monday|tuesday|wednesday|thursday|friday|saturday|sunday)\\\\s+at\\\\s+([a-z\\\\-]+\\\\s*${ampm})`, 'gi')\n];\n\nfor (const rx of patterns) {\n const m = tLower.match(rx);\n if (m) { agreedTime = m[0]; break; }\n}\n\n//// 8) Coarse normalization to an ISO datetime (fallbacks if needed)\nif (agreedTime) {\n const now = new Date();\n const scheduled = new Date(now.getTime() + 24 * 60 * 60 * 1000); // default = tomorrow\n\n const norm = agreedTime.replace(/\\./g, ''); // normalize a.m./p.m. dots away\n const slots = [\n { key: /\\b10\\s?:?00?\\s?am\\b/, h: 10 },\n { key: /\\beleven\\s?am\\b|\\b11\\s?:?00?\\s?am\\b/, h: 11 },\n { key: /\\b2\\s?:?00?\\s?pm\\b|\\btwo\\s?pm\\b/, h: 14 },\n { key: /\\b3\\s?:?00?\\s?pm\\b|\\bthree\\s?pm\\b/, h: 15 },\n ];\n let setHour = false;\n for (const s of slots) {\n if (s.key.test(norm)) { scheduled.setHours(s.h, 0, 0, 0); setHour = true; break; }\n }\n if (!setHour) {\n if (/\\bpm\\b/.test(norm)) scheduled.setHours(14, 0, 0, 0);\n else scheduled.setHours(10, 0, 0, 0);\n }\n agreedDateTime = scheduled.toISOString();\n}\n\n//// 9) Return normalized payload (includes record_id)\nreturn [{\n json: {\n call_connected: connected,\n call_duration: durationSec || 0,\n lead_interested: interested,\n agreed_time_text: agreedTime,\n agreed_datetime: agreedDateTime,\n transcript: transcriptText,\n\n lead_name: leadData.name,\n lead_email: leadData.email,\n lead_id: leadData.leadId, // optional, unused if you only match by record id\n lead_company: leadData.company,\n\n record_id: recordId, // <<< use this in Airtable updates\n disposition_tag: disposition,\n webhook_data: webhookData\n }\n}];\n"
},
"typeVersion": 2
},
{
"id": "47073473-14ff-419b-a41d-6e892ed444ca",
"name": "Check Call Success",
"type": "n8n-nodes-base.if",
"position": [
1632,
1312
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "89d7bb5c-55a5-42a1-bfb7-bc9783256c0f",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.call_success }}",
"rightValue": true
}
]
}
},
"typeVersion": 2.2
},
{
"id": "6c33f06d-642a-4302-966b-0d90272debfe",
"name": "Create Calendar event",
"type": "n8n-nodes-base.googleCalendar",
"position": [
2080,
1216
],
"parameters": {
"end": "={{ $json.end_iso }}",
"start": "={{ $json.start_iso }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "user@example.com",
"cachedResultName": "user@example.com"
},
"additionalFields": {
"summary": "=AI Automation Consultation - {{ $('Call Outcome Webhook').item.json.lead_name || 'Lead' }}"
}
},
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "810ec7c9-77d6-4452-8e90-dc9464a25ede",
"name": "Add Successful Call Record",
"type": "n8n-nodes-base.airtable",
"position": [
2304,
1216
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appXXXXXXXXXXXXXX",
"cachedResultUrl": "https://airtable.com/appXXXXXXXXXXXXXX",
"cachedResultName": "Your Lead Base"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblXXXXXXXXXXXXXX",
"cachedResultUrl": "https://airtable.com/appXXXXXXXXXXXXXX/tblXXXXXXXXXXXXXX",
"cachedResultName": "Leads"
},
"columns": {
"value": {
"Notes": "Call successful - Meeting scheduled for {{ $('Parse Call Outcome').item.json.agreed_time_text }}",
"Status": "Contacted",
"Message": "={{ $json.lead_message || $json.message_or_summary || '' }}\n",
"Meeting Date": "={{\n (() => {\n const fromCal = $items('Create Calendar event', 0, 0).json.start?.dateTime;\n const fromParse = $('Parse Call Outcome').item.json.agreed_datetime;\n const d = fromCal || fromParse;\n return d ? new Date(d).toISOString().slice(0,10) : null; // YYYY-MM-DD\n })()\n}}\n",
"Meeting Scheduled": "Yes"
},
"schema": [
{
"id": "id",
"type": "string",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "id",
"defaultMatch": true
},
{
"id": "Name",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Phone",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Phone",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Company",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Company",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Company Size",
"type": "options",
"display": true,
"options": [
{
"name": "1-10 people",
"value": "1-10 people"
},
{
"name": "11-50 people",
"value": "11-50 people"
},
{
"name": "51-200 people",
"value": "51-200 people"
},
{
"name": "201+ people",
"value": "201+ people"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Company Size",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "When do you need a solution?",
"type": "options",
"display": true,
"options": [
{
"name": "Immediately (within 2 weeks)",
"value": "Immediately (within 2 weeks)"
},
{
"name": "Soon (1-3 months)",
"value": "Soon (1-3 months)"
},
{
"name": "This year (3-12 months)",
"value": "This year (3-12 months)"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "When do you need a solution?",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "What's your role in the company?",
"type": "options",
"display": true,
"options": [
{
"name": "Owner/CEO/Founder",
"value": "Owner/CEO/Founder"
},
{
"name": "Manager/Director",
"value": "Manager/Director"
},
{
"name": "Employee/Team Member",
"value": "Employee/Team Member"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "What's your role in the company?",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Message",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Message",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Lead Score",
"type": "number",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Lead Score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Status",
"type": "options",
"display": true,
"options": [
{
"name": "New",
"value": "New"
},
{
"name": "Contacted",
"value": "Contacted"
},
{
"name": "Qualified",
"value": "Qualified"
},
{
"name": "Unqualified",
"value": "Unqualified"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Meeting Scheduled",
"type": "options",
"display": true,
"options": [
{
"name": "Yes",
"value": "Yes"
},
{
"name": "No",
"value": "No"
},
{
"name": "Pending",
"value": "Pending"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Meeting Scheduled",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Meeting Date",
"type": "dateTime",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Meeting Date",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Notes",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Notes",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Created",
"type": "string",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "Created",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Lead ID",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "Lead ID",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Record ID",
"type": "string",
"display": true,
"removed": false,
"readOnly": true,
"required": false,
"displayName": "Record ID",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {
"typecast": true
},
"operation": "update"
},
"credentials": {
"airtableTokenApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "9aecb6e7-0af0-41a0-9be6-98c4c8379228",
"name": "Send Booking ConfirmationEmail",
"type": "n8n-nodes-base.sendGrid",
"position": [
2528,
1216
],
"parameters": {
"subject": "Your AI automation consultation is confirmed!",
"toEmail": "={{ \n String(\n $json.fields?.Email \n || $('Parse Call Outcome').item.json.lead_email \n || $items('Call Outcome Webhook',0,0).json.body?.variables?.metadata?.lead_email \n || ''\n ).trim()\n}}\n",
"fromName": "Your Name - AI Automation",
"resource": "mail",
"fromEmail": "user@example.com",
"contentType": "text/html",
"contentValue": "=Hi {{ \n $json.fields?.Name \n || $('Parse Call Outcome').item.json.lead_name \n || 'there'\n}}, Great talking with you today! Your 15-minute AI automation consultation is confirmed for {{ $('Parse Call Outcome').item.json.agreed_time_text }}\n{{ (() => {\n const ev = $('Create Calendar event').item.json;\n if (!ev?.start?.dateTime) return ''; // nothing to add\n const tz = ev.start.timeZone || 'UTC';\n const pretty = new Date(ev.start.dateTime).toLocaleString('en-GB', {\n weekday: 'long', day: 'numeric', month: 'short', year: 'numeric',\n hour: 'numeric', minute: '2-digit', timeZone: tz\n });\n return `, ${pretty}`; // prepend the comma only if we have a value\n})() }}\n. I've sent you a calendar invite. Looking forward to our conversation!",
"additionalFields": {}
},
"credentials": {
"sendGridApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "0e48d014-b418-4040-a4a6-17b567a2906f",
"name": "Update Call Record",
"type": "n8n-nodes-base.airtable",
"position": [
3024,
640
],
"parameters": {
"base": {
"__rl": true,
"mode": "list",
"value": "appXXXXXXXXXXXXXX",
"cachedResultUrl": "https://airtable.com/appXXXXXXXXXXXXXX",
"cachedResultName": "Your Lead Base"
},
"table": {
"__rl": true,
"mode": "list",
"value": "tblXXXXXXXXXXXXXX",
"cachedResultUrl": "https://airtable.com/appXXXXXXXXXXXXXX/tblXXXXXXXXXXXXXX",
"cachedResultName": "Leads"
},
"columns": {
"value": {
"Notes": "Call attempted - Email sent as follow-up",
"Status": "Contacted",
"Message": "={{ $('Create a record').item.json.fields.Message }}",
"Record ID": "={{ $('Create a record').item.json.fields['Record ID'] }}",
"Lead Score": 0,
"Meeting Scheduled": "No"
},
"schema": [
{
"id": "id",
"type": "string",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "id",
"defaultMatch": true
},
{
"id": "Name",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Name",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Phone",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Phone",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Company",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Company",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Company Size",
"type": "options",
"display": true,
"options": [
{
"name": "1-10 people",
"value": "1-10 people"
},
{
"name": "11-50 people",
"value": "11-50 people"
},
{
"name": "51-200 people",
"value": "51-200 people"
},
{
"name": "201+ people",
"value": "201+ people"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Company Size",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "When do you need a solution?",
"type": "options",
"display": true,
"options": [
{
"name": "Immediately (within 2 weeks)",
"value": "Immediately (within 2 weeks)"
},
{
"name": "Soon (1-3 months)",
"value": "Soon (1-3 months)"
},
{
"name": "This year (3-12 months)",
"value": "This year (3-12 months)"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "When do you need a solution?",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "What's your role in the company?",
"type": "options",
"display": true,
"options": [
{
"name": "Owner/CEO/Founder",
"value": "Owner/CEO/Founder"
},
{
"name": "Manager/Director",
"value": "Manager/Director"
},
{
"name": "Employee/Team Member",
"value": "Employee/Team Member"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "What's your role in the company?",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Message",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Message",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Lead Score",
"type": "number",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Lead Score",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Status",
"type": "options",
"display": true,
"options": [
{
"name": "New",
"value": "New"
},
{
"name": "Contacted",
"value": "Contacted"
},
{
"name": "Qualified",
"value": "Qualified"
},
{
"name": "Unqualified",
"value": "Unqualified"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Meeting Scheduled",
"type": "options",
"display": true,
"options": [
{
"name": "Yes",
"value": "Yes"
},
{
"name": "No",
"value": "No"
},
{
"name": "Pending",
"value": "Pending"
}
],
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Meeting Scheduled",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Meeting Date",
"type": "dateTime",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "Meeting Date",
"d
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
airtableTokenApigoogleCalendarOAuth2ApiopenAiApisendGridApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This n8n template demonstrates how to capture inbound leads from a form, qualify them with OpenAI, and route the hottest ones to a Bland AI voice agent that calls them back, books a meeting on Google Calendar, and confirms by email, all without a human touching the lead.
Source: https://n8n.io/workflows/15779/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This powerful n8n automation workflow is designed to execute advanced B2B lead enrichment and hyper-personalization for cold email outreach. By orchestrating a complex chain of data scraping, AI analy
This template is perfect for e-commerce entrepreneurs, marketers, agencies, and creative teams who want to turn simple product photos and short descriptions into professional flyers or product videos—
AI-Powered Fake Review Detection Workflow Using n8n & Airtable. Uses httpRequest, airtable, openAi, slack. Webhook trigger; 27 nodes.
This workflow automates the end-to-end process of scheduling technical or behavioral interviews. It captures interview data via Webhook, creates a Google Calendar event with an integrated Google Meet
Automate your landscaping business’s lead follow-up and booking with this AI-powered GoHighLevel workflow. Designed by Hyrum Hurst, AI Automation Engineer at QuarterSmart, this template takes every ne