This workflow follows the Chainllm → Form Trigger 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 →
{
"name": "AI Form \u2192 Booked Call Agent (AutomateWithAli)",
"nodes": [
{
"parameters": {
"formTitle": "Book a call with us",
"formDescription": "Answer 4 quick questions \u2014 if we're a fit, you'll get a confirmed slot in minutes.",
"formFields": {
"values": [
{
"fieldLabel": "Your name",
"fieldType": "text",
"required": true
},
{
"fieldLabel": "Email",
"fieldType": "email",
"required": true
},
{
"fieldLabel": "What do you need help with?",
"fieldType": "textarea",
"required": true
},
{
"fieldLabel": "Budget range",
"fieldType": "dropdown",
"fieldOptions": {
"values": [
{
"option": "Under $500"
},
{
"option": "$500 - $2,000"
},
{
"option": "$2,000 - $10,000"
},
{
"option": "$10,000+"
}
]
},
"required": true
},
{
"fieldLabel": "How soon do you want to start?",
"fieldType": "dropdown",
"fieldOptions": {
"values": [
{
"option": "This week"
},
{
"option": "This month"
},
{
"option": "Just exploring"
}
]
},
"required": true
}
]
},
"options": {
"responseText": "Got it \u2014 check your inbox. If you're a fit, your call is already booked. \u26a1"
}
},
"id": "e533adc4-6870-4ea1-a145-a536996700cc",
"name": "New Lead Form",
"type": "n8n-nodes-base.formTrigger",
"typeVersion": 2.2,
"position": [
-1200,
300
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "a1",
"name": "businessName",
"type": "string",
"value": "YOUR BUSINESS NAME"
},
{
"id": "a2",
"name": "ownerEmail",
"type": "string",
"value": "you@yourbusiness.com"
},
{
"id": "a3",
"name": "services",
"type": "string",
"value": "LIST YOUR SERVICES, e.g. AI chatbots, workflow automation, lead-gen agents, CRM integrations"
},
{
"id": "a4",
"name": "fitCriteria",
"type": "string",
"value": "GOOD FIT RULES, e.g. needs one of our services; budget $500+; wants to start within a month"
},
{
"id": "a5",
"name": "timezone",
"type": "string",
"value": "Asia/Karachi"
},
{
"id": "a6",
"name": "replyStyle",
"type": "string",
"value": "Warm, short, confident. No buzzwords. Sound like a helpful human, never like a bot."
}
]
},
"options": {}
},
"id": "d8f9560b-0eae-47b9-9353-cb937c6929c7",
"name": "Business Setup",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-980,
300
]
},
{
"parameters": {
"messages": {
"messageValues": [
{
"type": "system",
"role": "system",
"message": "You are the lead-qualification brain for {{ $('Business Setup').item.json.businessName }}.\n\nWHAT WE SELL:\n{{ $('Business Setup').item.json.services }}\n\nWHO IS A GOOD FIT:\n{{ $('Business Setup').item.json.fitCriteria }}\n\nYOUR JOB, EVERY TIME, IN THIS ORDER:\n1. Read the form answers. Identify which of our services (if any) the lead actually needs. Never invent a need that is not written.\n2. Decide fit ONLY against the rules above. \"Under $500\" budgets or \"Just exploring\" timelines are NOT a fit unless the rules say otherwise.\n3. If the message is vague, empty, spam, or you genuinely cannot tell \u2014 do NOT guess. Output \"REVIEW\" so a human looks at it. A wrong booking is worse than a human check.\n4. Write a 1-2 sentence summary a business owner can skim in 5 seconds: what they want, budget, timeline, and the one thing worth saying on the call.\n\nOUTPUT CONTRACT \u2014 reply with ONLY this JSON object, nothing else, no markdown fences:\n{\n \"fit\": \"GOOD\" | \"NOT_FIT\" | \"REVIEW\",\n \"service\": \"the matching service from our list, or 'unclear'\",\n \"summary\": \"the 1-2 sentence skim-ready summary\",\n \"hook\": \"one short line for the confirmation email that reflects THEIR own words back\"\n}\n\nTONE REFERENCE FOR THE HOOK: {{ $('Business Setup').item.json.replyStyle }}"
},
{
"type": "user",
"role": "user",
"message": "New form submission:\n\nName: {{ $json['Your name'] }}\nEmail: {{ $json['Email'] }}\nNeeds: {{ $json['What do you need help with?'] }}\nBudget: {{ $json['Budget range'] }}\nTimeline: {{ $json['How soon do you want to start?'] }}\n\nReturn the JSON object only."
}
]
},
"options": {}
},
"id": "780c089f-6315-4a37-b597-930cec03ac9c",
"name": "Qualify Lead (AI)",
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"typeVersion": 1.5,
"position": [
-740,
300
]
},
{
"parameters": {
"model": {
"__rl": true,
"value": "gpt-4o-mini",
"mode": "list",
"cachedResultName": "GPT-4O-MINI"
},
"options": {
"responseFormat": "json_object",
"temperature": 0.2
}
},
"id": "044b1061-3418-411f-9ff1-45b653c742b9",
"name": "OpenAI Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [
-740,
520
],
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Reads the AI's JSON no matter how it's wrapped. Failure -> REVIEW (never guess).\nconst form = $('New Lead Form').first().json;\nconst setup = $('Business Setup').first().json;\nlet verdict = { fit: \"REVIEW\", service: \"unclear\", summary: \"AI answer unreadable \u2014 check manually.\", hook: \"\" };\ntry {\n let raw = ($json.text || $json.output || \"\").trim();\n raw = raw.replace(/```json|```/g, \"\").trim();\n const m = raw.match(/\\{[\\s\\S]*\\}/);\n if (m) {\n const p = JSON.parse(m[0]);\n if ([\"GOOD\", \"NOT_FIT\", \"REVIEW\"].includes(p.fit)) verdict = p;\n }\n} catch (e) { /* keep REVIEW default \u2014 a human checks, nobody gets lost */ }\nreturn [{ json: {\n name: form['Your name'], email: form['Email'],\n need: form['What do you need help with?'], budget: form['Budget range'],\n timeline: form['How soon do you want to start?'],\n fit: verdict.fit, service: verdict.service || \"unclear\",\n summary: verdict.summary || \"\", hook: verdict.hook || \"\",\n businessName: setup.businessName, ownerEmail: setup.ownerEmail,\n timezone: setup.timezone, booked: false, slotLabel: \"\"\n} }];"
},
"id": "717a7cfd-f522-4eb7-993a-6ca9f84b67f6",
"name": "Parse AI Answer",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-500,
300
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"id": "c1",
"leftValue": "={{ $json.fit }}",
"rightValue": "GOOD",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "a4bc3eac-9a9b-4bb8-a77e-a7479ec47d36",
"name": "Good Fit?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
-260,
300
]
},
{
"parameters": {
"resource": "event",
"operation": "getAll",
"calendar": {
"__rl": true,
"value": "primary",
"mode": "list",
"cachedResultName": "primary"
},
"returnAll": false,
"limit": 100,
"options": {
"timeMin": "={{ $now.toISO() }}",
"timeMax": "={{ $now.plus({ days: 6 }).toISO() }}",
"singleEvents": true,
"orderBy": "startTime"
}
},
"id": "fc34fcdc-784a-47c4-b4a5-144ef6181d36",
"name": "Fetch Calendar Window",
"type": "n8n-nodes-google.calendar",
"typeVersion": 1.2,
"position": [
-20,
160
],
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// Finds the first free 30-min slot: 10:00\u201318:00, Mon\u2013Fri, next 5 days, 15-min buffer.\n// Real calendar math in code \u2014 the AI never guesses dates.\nconst lead = $('Parse AI Answer').first().json;\nconst { DateTime } = require('luxon');\nconst TZ = lead.timezone || 'UTC';\nconst MEET = 30, BUF = 15, DAYS = 5, H0 = 10, H1 = 18;\nconst busy = $input.all().map(i => ({\n s: DateTime.fromISO(i.json.start?.dateTime || i.json.start?.date, { zone: TZ }),\n e: DateTime.fromISO(i.json.end?.dateTime || i.json.end?.date, { zone: TZ })\n})).filter(b => b.s.isValid && b.e.isValid);\nconst now = DateTime.now().setZone(TZ).plus({ hours: 2 }); // 2h notice, no \"call in 5 min\"\nlet chosen = null;\nouter:\nfor (let d = 0; d <= DAYS && !chosen; d++) {\n let day = now.plus({ days: d }).startOf('day');\n if (day.weekday > 5) continue; // Sat/Sun off\n for (let mins = H0 * 60; mins + MEET <= H1 * 60; mins += 30) {\n const s = day.plus({ minutes: mins }), e = s.plus({ minutes: MEET });\n if (e < now) continue;\n const clash = busy.some(b => s < b.e.plus({ minutes: BUF }) && e > b.s.minus({ minutes: BUF }));\n if (!clash) { chosen = { s, e }; break outer; }\n }\n}\nif (!chosen) return [{ json: { ...lead, slotFound: false } }];\nconst label = chosen.s.setZone(TZ).toFormat(\"ccc, LLL d 'at' h:mm a\");\nreturn [{ json: { ...lead, slotFound: true,\n slotStart: chosen.s.toISO(), slotEnd: chosen.e.toISO(), slotLabel: label } }];"
},
"id": "5677df3e-4db4-4def-9dc3-bbb6c512546b",
"name": "Find First Free Slot",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
220,
160
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"id": "c1",
"leftValue": "={{ $json.slotFound }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "true"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "c6077647-745b-46e1-a416-35a45d3a2bbe",
"name": "Free Slot Found?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
460,
160
]
},
{
"parameters": {
"resource": "event",
"operation": "create",
"calendar": {
"__rl": true,
"value": "primary",
"mode": "list",
"cachedResultName": "primary"
},
"start": "={{ $json.slotStart }}",
"end": "={{ $json.slotEnd }}",
"summary": "=Call: {{ $json.name }} \u00d7 {{ $json.businessName }}",
"description": "=Need: {{ $json.need }}\nBudget: {{ $json.budget }}\nTimeline: {{ $json.timeline }}\n\nAI summary: {{ $json.summary }}",
"additionalFields": {
"attendees": [
"={{ $json.email }}"
],
"sendUpdates": "all"
}
},
"id": "ee45e34d-cc32-4dfa-80ce-d52b786ae329",
"name": "Book the Call",
"type": "n8n-nodes-google.calendar",
"typeVersion": 1.2,
"position": [
700,
40
],
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "message",
"operation": "send",
"sendTo": "={{ $('Parse AI Answer').first().json.email }}",
"subject": "=You're booked: {{ $('Parse AI Answer').first().json.slotLabel }} \u2705",
"message": "=Hi {{ $('Parse AI Answer').first().json.name }},\n\nConfirmed \u2014 your call with {{ $('Parse AI Answer').first().json.businessName }} is booked for {{ $('Parse AI Answer').first().json.slotLabel }}.\nThe calendar invite is already on its way, so it's in both our calendars.\n\n{{ $('Parse AI Answer').first().json.hook }}\n\nNeed to move it? Just reply to this email.\n\n\u2014 {{ $('Parse AI Answer').first().json.businessName }}",
"options": {}
},
"id": "c1131158-4300-4a5d-a539-0e7f8848853f",
"name": "Send Confirmation Email",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
940,
40
],
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "b1",
"name": "booked",
"type": "boolean",
"value": true
}
]
},
"options": {},
"include": "allExcept",
"includeFields": ""
},
"id": "2fee690a-6061-4840-b74e-688e9f7b8159",
"name": "Mark Booked",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1180,
40
]
},
{
"parameters": {
"resource": "message",
"operation": "send",
"sendTo": "={{ $json.email }}",
"subject": "=You're qualified \u2014 grabbing you a time now",
"message": "=Hi {{ $json.name }},\n\nGood news \u2014 you're exactly who we work with. The calendar is just full this week, so a human is picking you a slot right now and will confirm today.\n\n{{ $json.hook }}\n\n\u2014 {{ $json.businessName }}",
"options": {}
},
"id": "9bfd3182-593e-4f73-8327-80f59cb9a253",
"name": "Send No-Slot Note",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
700,
320
],
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose"
},
"conditions": [
{
"id": "c1",
"leftValue": "={{ $json.fit }}",
"rightValue": "REVIEW",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "84ab38da-12e6-4e5b-a4bf-388859973d4a",
"name": "Needs Review?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
-20,
580
]
},
{
"parameters": {
"resource": "message",
"operation": "send",
"sendTo": "={{ $json.email }}",
"subject": "=Thanks for reaching out to {{ $json.businessName }}",
"message": "=Hi {{ $json.name }},\n\nThanks for telling us about your project. Right now it's not the perfect match for how we work \u2014 and we'd rather tell you that honestly than book a call that wastes your time.\n\nIf things change, reply here anytime.\n\n\u2014 {{ $json.businessName }}",
"options": {}
},
"id": "72e09f86-b927-469d-97bf-343505531647",
"name": "Send Kind Follow-Up",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
220,
700
],
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "message",
"operation": "send",
"sendTo": "={{ $json.ownerEmail }}",
"subject": "=\ud83d\udd0d Lead needs a human: {{ $json.name }}",
"message": "=The AI wasn't sure \u2014 your call, boss.\n\nName: {{ $json.name }}\nEmail: {{ $json.email }}\nNeed: {{ $json.need }}\nBudget: {{ $json.budget }}\nTimeline: {{ $json.timeline }}\n\nAI note: {{ $json.summary }}",
"options": {}
},
"id": "baaddfca-310c-430f-ae64-2885ca3ace05",
"name": "Alert Owner to Review",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
220,
480
],
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "row",
"operation": "create",
"tableId": "leads",
"fieldsUi": {
"fieldValues": [
{
"fieldId": "name",
"fieldValue": "={{ $json.name }}"
},
{
"fieldId": "email",
"fieldValue": "={{ $json.email }}"
},
{
"fieldId": "need",
"fieldValue": "={{ $json.need }}"
},
{
"fieldId": "budget",
"fieldValue": "={{ $json.budget }}"
},
{
"fieldId": "timeline",
"fieldValue": "={{ $json.timeline }}"
},
{
"fieldId": "fit",
"fieldValue": "={{ $json.fit }}"
},
{
"fieldId": "service",
"fieldValue": "={{ $json.service }}"
},
{
"fieldId": "ai_summary",
"fieldValue": "={{ $json.summary }}"
},
{
"fieldId": "booked",
"fieldValue": "={{ $json.booked === true }}"
},
{
"fieldId": "slot_label",
"fieldValue": "={{ $json.slotLabel || '' }}"
}
]
}
},
"id": "f7c1dbc3-3466-43dd-a079-f4de41758d83",
"name": "Save Lead in Supabase",
"type": "n8n-nodes-supabase",
"typeVersion": 1,
"position": [
700,
560
],
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
}
],
"connections": {
"New Lead Form": {
"main": [
[
{
"node": "Business Setup",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "Qualify Lead (AI)",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Business Setup": {
"main": [
[
{
"node": "Qualify Lead (AI)",
"type": "main",
"index": 0
}
]
]
},
"Qualify Lead (AI)": {
"main": [
[
{
"node": "Parse AI Answer",
"type": "main",
"index": 0
}
]
]
},
"Parse AI Answer": {
"main": [
[
{
"node": "Good Fit?",
"type": "main",
"index": 0
}
]
]
},
"Good Fit?": {
"main": [
[
{
"node": "Fetch Calendar Window",
"type": "main",
"index": 0
}
],
[
{
"node": "Needs Review?",
"type": "main",
"index": 0
}
]
]
},
"Fetch Calendar Window": {
"main": [
[
{
"node": "Find First Free Slot",
"type": "main",
"index": 0
}
]
]
},
"Find First Free Slot": {
"main": [
[
{
"node": "Free Slot Found?",
"type": "main",
"index": 0
}
]
]
},
"Free Slot Found?": {
"main": [
[
{
"node": "Book the Call",
"type": "main",
"index": 0
}
],
[
{
"node": "Send No-Slot Note",
"type": "main",
"index": 0
}
]
]
},
"Book the Call": {
"main": [
[
{
"node": "Send Confirmation Email",
"type": "main",
"index": 0
}
]
]
},
"Send Confirmation Email": {
"main": [
[
{
"node": "Mark Booked",
"type": "main",
"index": 0
}
]
]
},
"Needs Review?": {
"main": [
[
{
"node": "Alert Owner to Review",
"type": "main",
"index": 0
}
],
[
{
"node": "Send Kind Follow-Up",
"type": "main",
"index": 0
}
]
]
},
"Mark Booked": {
"main": [
[
{
"node": "Save Lead in Supabase",
"type": "main",
"index": 0
}
]
]
},
"Send No-Slot Note": {
"main": [
[
{
"node": "Save Lead in Supabase",
"type": "main",
"index": 0
}
]
]
},
"Send Kind Follow-Up": {
"main": [
[
{
"node": "Save Lead in Supabase",
"type": "main",
"index": 0
}
]
]
},
"Alert Owner to Review": {
"main": [
[
{
"node": "Save Lead in Supabase",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"timezone": "Asia/Karachi"
},
"meta": {
"templateCredsSetupCompleted": false
}
}
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.
gmailOAuth2googleCalendarOAuth2ApiopenAiApisupabaseApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
AI Form → Booked Call Agent (AutomateWithAli). Uses formTrigger, chainLlm, lmChatOpenAi, n8n-nodes-google. Event-driven trigger; 17 nodes.
Source: https://github.com/ar-automation-labs/free-n8n-workflow-templates/blob/main/ai-agent-for-booking-leads/ai-form-to-booked-call-n8n.json — 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.
Template Nodes Example. Uses CUSTOM, formTrigger, executeWorkflowTrigger, chatTrigger. Event-driven trigger; 70 nodes.
This workflow streamlines how employees request equipment/items and how those requests reach the Procurement team. It validates the employee by enrollment number, detects whether a manager exists, and
This workflow is perfect for: Agile development teams and project managers who need to quickly set up Jira projects Product managers who want to convert feature ideas into structured user stories and
This workflow creates an automated system for monitoring and receiving notifications about new videos from your favorite YouTube channels through RSS feeds, with customizable email and Telegram notifi
The workflow runs every hour with a randomized delay of 5–20 minutes to help distribute load. It records the exact date and time a lead is emailed so you can track outreach. Follow-ups are automatical