Visual Workflow

    JSON Code

    {
      "id": "91v9awyaUNuZ80wd",
      "meta": {
        "instanceId": "9fb634bb746cbe3fe9f7fa86fdd635510d2f0c24c4ba919fbba1ca6aec3c4e57",
        "templateCredsSetupCompleted": true
      },
      "name": "AI Cold Caller - Workflow - Make Call",
      "tags": [],
      "nodes": [
        {
          "id": "32728bee-c3e1-42ba-8284-a9a8f05e2389",
          "name": "When clicking ‘Execute workflow’",
          "type": "n8n-nodes-base.manualTrigger",
          "position": [
            -912,
            80
          ],
          "parameters": {},
          "typeVersion": 1
        },
        {
          "id": "13f5c39d-ae49-41da-98e8-450618913e8c",
          "name": "Loop Over Items",
          "type": "n8n-nodes-base.splitInBatches",
          "position": [
            -208,
            400
          ],
          "parameters": {
            "options": {}
          },
          "typeVersion": 3
        },
        {
          "id": "729380da-1f14-4dd7-b340-b2b5d2561100",
          "name": "Get Contacts",
          "type": "n8n-nodes-base.googleSheets",
          "position": [
            -400,
            400
          ],
          "parameters": {
            "options": {},
            "sheetName": {
              "__rl": true,
              "mode": "list",
              "value": 855027580,
              "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1OSg3JyIHWvvjvY9K2mfu2DWPAwkFt6dI0M6-5dcSqPk/edit#gid=855027580",
              "cachedResultName": "Contacts"
            },
            "documentId": {
              "__rl": true,
              "mode": "list",
              "value": "1OSg3JyIHWvvjvY9K2mfu2DWPAwkFt6dI0M6-5dcSqPk",
              "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1OSg3JyIHWvvjvY9K2mfu2DWPAwkFt6dI0M6-5dcSqPk/edit?usp=drivesdk",
              "cachedResultName": "Cold Call Cake Business - n8n"
            }
          },
          "credentials": {
            "googleSheetsOAuth2Api": {
              "id": "TB2tCNqF1GBm2ag8",
              "name": "Google Sheets account"
            }
          },
          "typeVersion": 4.7
        },
        {
          "id": "3506caaf-4f6f-4629-903e-3697f917d714",
          "name": "Make Vapi Call",
          "type": "n8n-nodes-base.httpRequest",
          "maxTries": 2,
          "position": [
            192,
            416
          ],
          "parameters": {
            "url": "https://api.vapi.ai/call",
            "method": "POST",
            "options": {},
            "jsonBody": "={{ $json.data }}",
            "sendBody": true,
            "specifyBody": "json",
            "authentication": "genericCredentialType",
            "genericAuthType": "httpBearerAuth"
          },
          "credentials": {
            "httpBearerAuth": {
              "id": "DvbTOgvkcULlmmzE",
              "name": "Vapi Auth Bearer account"
            }
          },
          "retryOnFail": true,
          "typeVersion": 4.2
        },
        {
          "id": "593dbadc-ba1c-48f8-ab68-60413b4abddc",
          "name": "Format Payload",
          "type": "n8n-nodes-base.code",
          "position": [
            16,
            416
          ],
          "parameters": {
            "jsCode": "const { Name, FormatedPhone } = $input.first().json\nconst todayDate = $now.toISODate()\nconst available_slots = $('Booking Vars').first().json.available_slots\n\nconst data = JSON.stringify({\n  phoneNumberId: \"ca3182f4-297c-4914-85c4-7be5af1b8abc\",\n  customer: {\n    number: `${FormatedPhone}`,\n    name: `${Name}`,\n  },\n  assistant: {\n    name: \"Emma\",\n    voice: {\n      voiceId: \"Paige\",\n      provider: \"vapi\"\n    },\n    model: {\n      model: \"gpt-4o\",\n      provider: \"openai\",\n      toolIds: [\n        \"91c2dce8-5d0c-427c-8009-238de0aacc23\"\n      ],\n      messages: [\n        {\n          role: \"system\",\n          content: `\n### CONTEXT\nBusiness Name: ${Name}\nBusiness Number: ${FormatedPhone}\nToday's Date: ${todayDate}\nAvailable Booking Slots: ${available_slots}\n\nYou are **Emma**, a friendly business development assistant from **WebLabs**, a website and software agency that helps restaurants and local businesses grow online.\n\n---\n\n### GOAL\nCall small restaurants or local businesses that don't have a website.\nExplain briefly why you're calling, gauge interest, and — if interested — schedule a **consultation call** by collecting their **name** and **email**.\n\n---\n\n### LOGIC FOR SCHEDULING\n1. Always **offer the available slots from context first**:\n   - Use the list in **Available Booking Slots**.\n   - Example: \"Looks like we have Tuesday at 3 PM or Wednesday at 11 AM available — which works better for you?\"\n\n2. If the customer says they’re not available for any of these slots:\n   - Use the **check-cal-booking-availability** tool to find more time options.\n   - Offer those new slots politely.\n\n---\n\n### INTRO (Short & Natural)\n\"Hi, this is Emma from WebLabs. The reason for my call is we’re reaching out to local businesses that don’t yet have a website. Would you be open to a quick chat with our consultant to see how we can help you get online?\"\n\nIf they say they’re busy:\n\"No problem at all — when would be a better time for a quick follow-up?\"\n\n---\n\n### IF INTERESTED\n\"That’s great! Could I get your **name** and **best email** so we can send over a link to schedule your call?\"\n\nThen offer the available slots (using the logic above).\n\n---\n\n### IF NOT INTERESTED\n\"Totally understand! Thanks so much for your time — have a great day.\"\n\n---\n\n### RULES\n- Keep it friendly, confident, and conversational.\n- Never argue or pressure.\n- Always thank the person before ending the call.\n- Use the context slot list first, only call **check-cal-booking-availability** if needed.\n          `\n        }\n      ]\n    },\n    firstMessage: `Hi ${Name}, this is Emma from WebLabs. I was trying to check out your menu on your website, but it seems the site isn’t live yet.`,\n    voicemailMessage: \"Hi, this is Emma from WebLabs. I wanted to discuss how a website can help you attract more customers. Feel free to call back anytime!\",\n    endCallMessage: \"Thanks for your time! I’ll send over the Calendly link if you’re interested. Have a great day!\",\n    transcriber: {\n      model: \"nova-2\",\n      language: \"en\",\n      provider: \"deepgram\"\n    },\n    compliancePlan: {\n      hipaaEnabled: false,\n      pciEnabled: false\n    }\n  }\n})\n\nreturn {\n  json: { data }\n}\n"
          },
          "typeVersion": 2
        },
        {
          "id": "867666bb-7aae-4840-87b7-7951a61558a7",
          "name": "Update Contact List",
          "type": "n8n-nodes-base.googleSheets",
          "position": [
            368,
            416
          ],
          "parameters": {
            "columns": {
              "value": {
                "isCalled": "Yes",
                "FormatedPhone": "={{ $json.customer.number }}"
              },
              "schema": [
                {
                  "id": "Name",
                  "type": "string",
                  "display": true,
                  "removed": true,
                  "required": false,
                  "displayName": "Name",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "Phone",
                  "type": "string",
                  "display": true,
                  "removed": true,
                  "required": false,
                  "displayName": "Phone",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "FormatedPhone",
                  "type": "string",
                  "display": true,
                  "removed": false,
                  "required": false,
                  "displayName": "FormatedPhone",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "isCalled",
                  "type": "string",
                  "display": true,
                  "removed": false,
                  "required": false,
                  "displayName": "isCalled",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "isAnswered",
                  "type": "string",
                  "display": true,
                  "removed": true,
                  "required": false,
                  "displayName": "isAnswered",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "isVoiceMail",
                  "type": "string",
                  "display": true,
                  "removed": true,
                  "required": false,
                  "displayName": "isVoiceMail",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "CallSummary",
                  "type": "string",
                  "display": true,
                  "removed": true,
                  "required": false,
                  "displayName": "CallSummary",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "MeetingLink",
                  "type": "string",
                  "display": true,
                  "removed": true,
                  "required": false,
                  "displayName": "MeetingLink",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "Address",
                  "type": "string",
                  "display": true,
                  "removed": true,
                  "required": false,
                  "displayName": "Address",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "Bing Maps URL",
                  "type": "string",
                  "display": true,
                  "removed": true,
                  "required": false,
                  "displayName": "Bing Maps URL",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "row_number",
                  "type": "number",
                  "display": true,
                  "removed": true,
                  "readOnly": true,
                  "required": false,
                  "displayName": "row_number",
                  "defaultMatch": false,
                  "canBeUsedToMatch": true
                }
              ],
              "mappingMode": "defineBelow",
              "matchingColumns": [
                "FormatedPhone"
              ],
              "attemptToConvertTypes": false,
              "convertFieldsToString": false
            },
            "options": {},
            "operation": "update",
            "sheetName": {
              "__rl": true,
              "mode": "list",
              "value": 855027580,
              "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1OSg3JyIHWvvjvY9K2mfu2DWPAwkFt6dI0M6-5dcSqPk/edit#gid=855027580",
              "cachedResultName": "Contacts"
            },
            "documentId": {
              "__rl": true,
              "mode": "list",
              "value": "1OSg3JyIHWvvjvY9K2mfu2DWPAwkFt6dI0M6-5dcSqPk",
              "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1OSg3JyIHWvvjvY9K2mfu2DWPAwkFt6dI0M6-5dcSqPk/edit?usp=drivesdk",
              "cachedResultName": "Cold Call Cake Business - n8n"
            }
          },
          "credentials": {
            "googleSheetsOAuth2Api": {
              "id": "TB2tCNqF1GBm2ag8",
              "name": "Google Sheets account"
            }
          },
          "typeVersion": 4.7
        },
        {
          "id": "459c4305-3ef8-44a5-a8e1-657e48cf8a6d",
          "name": "Set Vars",
          "type": "n8n-nodes-base.set",
          "position": [
            -688,
            80
          ],
          "parameters": {
            "options": {},
            "assignments": {
              "assignments": [
                {
                  "id": "4b6cf6e0-8f45-487e-ba6e-05381dd9fc1c",
                  "name": "cal_call_id",
                  "type": "number",
                  "value": null
                },
                {
                  "id": "6cefa32a-2bda-434f-a42e-0aed6fd6abc1",
                  "name": "cal_call_name",
                  "type": "string",
                  "value": ""
                },
                {
                  "id": "e82e38dd-b2d1-45e5-a0ad-a7a58a98ce8d",
                  "name": "cal_call_min_duration",
                  "type": "number",
                  "value": null
                },
                {
                  "id": "d1930405-9e13-46c3-91c2-8a5e65d3fd32",
                  "name": "cal_call_username",
                  "type": "string",
                  "value": ""
                },
                {
                  "id": "02ba9870-629a-4cc3-ad29-4325f2cf1e2d",
                  "name": "cal_call_timezone",
                  "type": "string",
                  "value": "America/New_York"
                }
              ]
            }
          },
          "typeVersion": 3.4
        },
        {
          "id": "e730d757-7005-4b30-ae97-f6ba6fa63004",
          "name": "Get Other Booking Slots",
          "type": "n8n-nodes-base.httpRequest",
          "position": [
            -240,
            80
          ],
          "parameters": {
            "url": "https://api.cal.com/v2/slots",
            "options": {},
            "sendQuery": true,
            "sendHeaders": true,
            "authentication": "genericCredentialType",
            "genericAuthType": "httpHeaderAuth",
            "queryParameters": {
              "parameters": [
                {
                  "name": "eventTypeSlug",
                  "value": "={{ $('Set Vars').item.json.cal_call_name }}"
                },
                {
                  "name": "username",
                  "value": "={{ $('Set Vars').item.json.cal_call_username }}"
                },
                {
                  "name": "end",
                  "value": "={{ $json.endDate }}"
                },
                {
                  "name": "start",
                  "value": "={{ $json.startDate }}"
                }
              ]
            },
            "headerParameters": {
              "parameters": [
                {
                  "name": "cal-api-version",
                  "value": "=2024-09-04"
                }
              ]
            }
          },
          "credentials": {
            "httpHeaderAuth": {
              "id": "tlMMaHIx7zqf0LCc",
              "name": "Cal Header Auth"
            }
          },
          "typeVersion": 4.2
        },
        {
          "id": "58b49d2d-8f2c-4351-a524-f30a2ab1b944",
          "name": "Code in JavaScript",
          "type": "n8n-nodes-base.code",
          "position": [
            -464,
            80
          ],
          "parameters": {
            "jsCode": "const startDate = new Date();\nconst endDate = new Date(startDate.getTime() + 4 * 24 * 60 * 60 * 1000);\n\n\nreturn {\n  json:{\n    startDate:startDate.toISOString(),\n    endDate:endDate.toISOString()\n  }\n}\n"
          },
          "typeVersion": 2
        },
        {
          "id": "9cbb89c5-8a04-4cca-a351-e7597061e431",
          "name": "Format Booking Slots",
          "type": "@n8n/n8n-nodes-langchain.agent",
          "position": [
            -912,
            400
          ],
          "parameters": {
            "text": "=Here are the available booking slots in JSON format:\n{{ $json.data }}\n\nPlease tell the customer in natural language what the next available booking timings are.\n\nToday is {{ $now }}",
            "options": {
              "systemMessage": "AI Receptionist: Humanize Booking Availability\n\nYou are an AI receptionist that helps customers find the next available booking times.\n\nThe input will always be provided in JSON format containing booking slot information such as start, end, service, and location.\n\nYour task:\n1. Read and interpret the JSON input (list of available booking slots).\n2. Convert the time and date into **clear, conversational text** for the customer.\n3. Combine slots on the same day in one sentence where possible.\n4. Be friendly, polite, and concise — like a real receptionist.\n5. Mention \"tomorrow\", \"day after tomorrow\", or weekday names where possible, instead of exact dates.\n6. Only output **natural language text**, no JSON or code.\n7. Format times in 12-hour format with AM/PM and day indicators.\n\nExample:\nIf JSON says:\n[\n  {\"start\": \"2025-10-28T14:00:00+05:30\"},\n  {\"start\": \"2025-10-28T16:00:00+05:30\"},\n  {\"start\": \"2025-10-28T18:00:00+05:30\"},\n  {\"start\": \"2025-10-29T16:00:00+05:30\"}\n]\nThen output:\n\"The next available booking slots are tomorrow at 2 PM, 4 PM, and 6 PM, and the day after tomorrow at 4 PM.\"\n\nIf no slots are available:\nReply: \"I'm sorry, there are no available slots in the next few days. Would you like me to check further ahead?\""
            },
            "promptType": "define"
          },
          "typeVersion": 2.2
        },
        {
          "id": "c43eb88b-aae4-440a-94f1-ca11556ec03b",
          "name": "OpenRouter Chat Model2",
          "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
          "position": [
            -912,
            640
          ],
          "parameters": {
            "options": {}
          },
          "credentials": {
            "openRouterApi": {
              "id": "6z6zYFNsqyqFRUEB",
              "name": "OpenRouter account"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "ed768534-d5ea-40d4-b43c-614bccc2b2c4",
          "name": "Format Data",
          "type": "n8n-nodes-base.code",
          "position": [
            -16,
            80
          ],
          "parameters": {
            "jsCode": "let data = $input.first().json.data\ndata = JSON.stringify(data)\n\nreturn {\n  json:{\n    data\n  }\n}"
          },
          "typeVersion": 2
        },
        {
          "id": "f4bba494-9732-4da6-ad22-844a5f6ca6a3",
          "name": "Booking Vars",
          "type": "n8n-nodes-base.set",
          "position": [
            -624,
            400
          ],
          "parameters": {
            "options": {},
            "assignments": {
              "assignments": [
                {
                  "id": "2ca67f05-a570-4115-8bc4-ab342b15bd77",
                  "name": "available_slots",
                  "type": "string",
                  "value": "={{ $json.output }}"
                }
              ]
            }
          },
          "typeVersion": 3.4
        },
        {
          "id": "059ba846-4b26-49fe-be28-b888d637b9ac",
          "name": "Sticky Note",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            -960,
            320
          ],
          "parameters": {
            "color": 6,
            "width": 480,
            "height": 480,
            "content": "## Get Booking Slots\n"
          },
          "typeVersion": 1
        },
        {
          "id": "74c4d050-286b-4dcd-982a-b2360e3ee3b1",
          "name": "Sticky Note1",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            -448,
            320
          ],
          "parameters": {
            "color": 5,
            "width": 1008,
            "height": 400,
            "content": "## Call Leads"
          },
          "typeVersion": 1
        },
        {
          "id": "84f80d1c-6db5-4d17-92a5-b7d5959fcdda",
          "name": "Sticky Note2",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            -960,
            -16
          ],
          "parameters": {
            "width": 1520,
            "height": 304,
            "content": "## Get Leads"
          },
          "typeVersion": 1
        },
        {
          "id": "777fdc5e-a5d4-4f81-ba5b-1d7b1a32dbd9",
          "name": "Sticky Note9",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            -1488,
            -16
          ],
          "parameters": {
            "width": 500,
            "height": 1152,
            "content": "# 🛠️ Setup Guide\n---\n\n**Author: [Sohail Jafri](https://link.thesohailjafri.me/web_ai) | [Youtube](https://link.thesohailjafri.me/yt_ai) | [Instagram](https://link.thesohailjafri.me/ig_ai) | [Join Skool](https://link.thesohailjafri.me/join-skool)**\n\n## 🚀 AI Cold Caller — Make Call (Setup Guide)\n\n### 1. Connect Required Credentials\n- **Google Sheets OAuth** → Reads & updates lead list  \n- **Vapi Bearer Auth** → Makes outbound phone calls  \n- **OpenRouter API Key** → Formats booking slots  \n- **Cal.com Header Auth** → Fetches available booking slots  \n\n---\n\n### 2. Setup Google Sheet\n- [Copy Sheet Template](https://docs.google.com/spreadsheets/d/1OSg3JyIHWvvjvY9K2mfu2DWPAwkFt6dI0M6-5dcSqPk/copy)\n\n---\n\n### 3. Configure Booking Variables\nIn **Set Vars** node, ensure:\n- `cal_call_id`\n- `cal_call_name`\n- `cal_call_min_duration`\n- `cal_call_username`\n- `cal_call_timezone`\n\nMatch these with your **Cal.com Event Type** settings.\n\n---\n\n### 4. Ensure Booking Slots Flow Works\nNodes that must be connected properly:\n- **Get Other Booking Slots**\n- **Format Booking Slots**\n- **Booking Vars**\n\nThese provide human-friendly slot text to Vapi.\n\n---\n\n## ☕ Buy Me a Coffee\nhttps://link.thesohailjafri.me/buymeacoffee\n"
          },
          "typeVersion": 1
        }
      ],
      "active": false,
      "pinData": {},
      "settings": {
        "executionOrder": "v1"
      },
      "versionId": "6f3dc33f-9dd8-4735-acdb-807d63568a4f",
      "connections": {
        "Set Vars": {
          "main": [
            [
              {
                "node": "Code in JavaScript",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Format Data": {
          "main": [
            [
              {
                "node": "Format Booking Slots",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Booking Vars": {
          "main": [
            [
              {
                "node": "Get Contacts",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Get Contacts": {
          "main": [
            [
              {
                "node": "Loop Over Items",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Format Payload": {
          "main": [
            [
              {
                "node": "Make Vapi Call",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Make Vapi Call": {
          "main": [
            [
              {
                "node": "Update Contact List",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Loop Over Items": {
          "main": [
            [],
            [
              {
                "node": "Format Payload",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Code in JavaScript": {
          "main": [
            [
              {
                "node": "Get Other Booking Slots",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Update Contact List": {
          "main": [
            [
              {
                "node": "Loop Over Items",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Format Booking Slots": {
          "main": [
            [
              {
                "node": "Booking Vars",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "OpenRouter Chat Model2": {
          "ai_languageModel": [
            [
              {
                "node": "Format Booking Slots",
                "type": "ai_languageModel",
                "index": 0
              }
            ]
          ]
        },
        "Get Other Booking Slots": {
          "main": [
            [
              {
                "node": "Format Data",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "When clicking ‘Execute workflow’": {
          "main": [
            [
              {
                "node": "Set Vars",
                "type": "main",
                "index": 0
              }
            ]
          ]
        }
      }
    }