AutomationFlowsAI & RAG › Outbound Sales Calls From Google Sheets With Retell AI

Outbound Sales Calls From Google Sheets With Retell AI

Original n8n title: Make Outbound Sales Calls From Google Sheets Using a Retell AI Voice Agent

ByMarcus Taylor @intellagents on n8n.io

This n8n workflow automates outbound phone calls to new leads using Retell AI, with built-in timezone detection to ensure you're only calling during business hours.

Event trigger★★★★☆ complexity18 nodesHTTP RequestGoogle Sheets TriggerGoogle Sheets
AI & RAG Trigger: Event Nodes: 18 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #12856 — we link there as the canonical source.

This workflow follows the Google Sheets → Googlesheetstrigger 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 →

Download .json
{
  "name": "Outbound sales calls from Google Sheets using Retell AI voice agent",
  "nodes": [
    {
      "id": "3033d295-aca5-4e11-8014-5a5529272046",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        496,
        912
      ],
      "parameters": {
        "color": 4,
        "height": 432,
        "content": "## Make Phone Call\n**\u2699\ufe0f Setup:**\n**Create a free Retell account**  [Retell AI \u2013 $10 FREE credits](https://dashboard.retellai.com/?ref=retell-n8n) \n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "08e73ac6-8da3-4902-9d95-eeef09c82461",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1136,
        912
      ],
      "parameters": {
        "color": 5,
        "width": 288,
        "height": 432,
        "content": "## When a lead arrives\n"
      },
      "typeVersion": 1
    },
    {
      "id": "df3b824e-aaad-48b9-b814-a246f82178ea",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -832,
        912
      ],
      "parameters": {
        "color": 6,
        "width": 576,
        "height": 432,
        "content": "## Santise the phone number\nPhone numbers are messy; For the call to work, we need to strip away all spaces, dashes, plus signs, and brackets so all we have left are numbers. Next, we're filtering out any numbers we've already called for a bit of damage control.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "75df0ca6-fb3d-4252-ba5a-dd59c49e501f",
      "name": "Make phone call",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        560,
        1056
      ],
      "parameters": {
        "url": "https://api.retellai.com/v2/create-phone-call",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "from_number",
              "value": "{replace with your Retell AI phone number}"
            },
            {
              "name": "to_number",
              "value": "={{ $json['Phone Number'] }}"
            },
            {
              "name": "agent_id",
              "value": "{replace with your Retell AI agent ID}"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "fb28e354-faa1-4e4e-9962-474551fd7a04",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        752,
        912
      ],
      "parameters": {
        "color": 4,
        "width": 720,
        "height": 432,
        "content": "## Wait for the call to finish\nThis poll's Retell AI until the call has finished. For a more elegant approach, you can use [a webhook](https://docs.retellai.com/features/webhook-overview) but this approach is simpler / beginner-friendly.\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "77f2dd86-1d67-4cb8-8524-c998cbb3ca6c",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -240,
        912
      ],
      "parameters": {
        "color": 6,
        "width": 720,
        "height": 432,
        "content": "## Check it's daytime (in their timezone)\nWe don't want to call leads at 2am, so this step checks the lead's timezone (based on their phone number) and only calls within the 8am-5pm window."
      },
      "typeVersion": 1
    },
    {
      "id": "cf181a33-7778-41ba-b395-b75e1df6b796",
      "name": "Get lead's timezone",
      "type": "n8n-nodes-base.code",
      "position": [
        -160,
        1072
      ],
      "parameters": {
        "jsCode": "const rawPhone = $input.first().json['Phone Number'];\nconst phoneNumber = String(rawPhone || '');\nconst cleaned = phoneNumber.replace(/\\D/g, '');\n\n// Country code to timezone mapping\nconst timezoneMap = {\n  '44': 'Europe/London',\n  '1': 'America/New_York',\n  '61': 'Australia/Sydney',\n  '33': 'Europe/Paris',\n  '49': 'Europe/Berlin',\n  '34': 'Europe/Madrid',\n  '39': 'Europe/Rome',\n  '31': 'Europe/Amsterdam',\n  '353': 'Europe/Dublin',\n  '91': 'Asia/Kolkata',\n  '971': 'Asia/Dubai',\n  '65': 'Asia/Singapore',\n  '81': 'Asia/Tokyo',\n};\n\n// Find matching country code (check longer codes first)\nlet timezone = 'Europe/London';\nlet countryCode = '44';\n\nconst sortedCodes = Object.keys(timezoneMap).sort((a, b) => b.length - a.length);\nfor (const code of sortedCodes) {\n  if (cleaned.startsWith(code)) {\n    timezone = timezoneMap[code];\n    countryCode = code;\n    break;\n  }\n}\n\n// Get current time in the lead's timezone using Intl API\nconst now = new Date();\n\nconst formatter = new Intl.DateTimeFormat('en-GB', {\n  timeZone: timezone,\n  hour: 'numeric',\n  minute: 'numeric',\n  weekday: 'long',\n  hour12: false\n});\n\nconst parts = formatter.formatToParts(now);\nconst localHour = parseInt(parts.find(p => p.type === 'hour').value, 10);\nconst localMinute = parseInt(parts.find(p => p.type === 'minute').value, 10);\nconst weekdayName = parts.find(p => p.type === 'weekday').value;\n\nconst weekdayMap = {\n  'Monday': 1,\n  'Tuesday': 2,\n  'Wednesday': 3,\n  'Thursday': 4,\n  'Friday': 5,\n  'Saturday': 6,\n  'Sunday': 7\n};\nconst dayOfWeek = weekdayMap[weekdayName];\n\nreturn {\n  json: {\n    ...$input.first().json,\n    timezone,\n    countryCode,\n    localHour,\n    localMinute,\n    dayOfWeek,\n    localTimeFormatted: `${weekdayName} ${String(localHour).padStart(2, '0')}:${String(localMinute).padStart(2, '0')}`\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "7e839392-632c-49d9-bd68-521cc0994a7d",
      "name": "Cleanse phone numbers",
      "type": "n8n-nodes-base.code",
      "position": [
        -736,
        1072
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\nconst updatedItems = items.map((item) => {\n  item.json[\"Phone Number\"] = String(item.json[\"Phone Number\"]).replace(/\\D/g, \"\");\n  return item;\n});\nreturn updatedItems;"
      },
      "typeVersion": 2
    },
    {
      "id": "2a8f1a88-5638-400e-a535-db4ba9d5ff13",
      "name": "New lead",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "position": [
        -1056,
        1072
      ],
      "parameters": {
        "event": "rowAdded",
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultUrl": "",
          "cachedResultName": ""
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "67dc5f93-74ea-45ea-b698-629268a9d7cf",
      "name": "Is it between 8am-5pm for them?",
      "type": "n8n-nodes-base.if",
      "position": [
        96,
        1072
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "ba0908e3-7234-4e40-b232-37111a59ff11",
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $json.localHour }}",
              "rightValue": 8
            },
            {
              "id": "5085c2ab-cf18-41d9-989e-1f25579a89b8",
              "operator": {
                "type": "number",
                "operation": "lte"
              },
              "leftValue": "={{ $json.localHour }}",
              "rightValue": 17
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "1b1ce178-dca3-4b6c-be73-56de95f1fe46",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        304,
        1152
      ],
      "parameters": {
        "unit": "hours",
        "amount": 2
      },
      "typeVersion": 1.1
    },
    {
      "id": "5de17c52-cbc4-4786-8fa4-2570eea19ae7",
      "name": "Wait 1 minute",
      "type": "n8n-nodes-base.wait",
      "position": [
        848,
        1056
      ],
      "parameters": {
        "unit": "minutes",
        "amount": 1
      },
      "typeVersion": 1.1
    },
    {
      "id": "b97b854d-9950-473d-b5d4-5d81a35a245c",
      "name": "Call has finished",
      "type": "n8n-nodes-base.if",
      "position": [
        1232,
        1056
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond1",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{$json.body.call_status}}",
              "rightValue": "ended"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "29c2d2f2-80f0-4b65-846d-faee8ccb9ac2",
      "name": "Get Call Status",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1040,
        1056
      ],
      "parameters": {
        "url": "={{`https://api.retellai.com/v2/get-call/${$node['Make phone call'].json.call_id}`}}\n",
        "options": {
          "response": {
            "response": {
              "fullResponse": true
            }
          }
        },
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "headerParameters": {
          "parameters": [
            {}
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "c97bd9e5-b038-4c36-826e-b069d21f73b1",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1488,
        912
      ],
      "parameters": {
        "color": 7,
        "width": 512,
        "height": 432,
        "content": "## Update Google Sheet\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "23763512-2dee-4728-8b6d-0df2fee84e71",
      "name": "Filter numbers we've already called",
      "type": "n8n-nodes-base.filter",
      "position": [
        -432,
        1072
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "81d18bd8-2b72-4605-9503-45c6cd0de270",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json['Phone Number'] }}",
              "rightValue": 0
            },
            {
              "id": "2f60a482-7bac-441a-b8de-35e62238d874",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.Status }}",
              "rightValue": "Not Called"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "dd47c895-1915-4227-b163-c991ff4cd29d",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1792,
        640
      ],
      "parameters": {
        "width": 592,
        "height": 1040,
        "content": "## \ud83d\udcde Automated Outbound Lead Caller\n\nAutomatically call new leads from a Google Sheet using [Retell AI](https://www.retellai.com/?via=intellagents), with built-in timezone awareness to ensure you're only calling during business hours.\n\n### What it does\n1. **Triggers** when a new row is added to your Google Sheet\n2. **Cleans** the phone number (removes spaces, dashes, brackets)\n3. **Checks timezone** based on country code\u2014only calls between 8am-5pm local time\n4. **Makes the call** via Retell AI using your configured voice agent\n5. **Polls for completion** then updates the sheet with \"Called\" status, a call summary, sentiment, transcript & more.\n\n### \u2699\ufe0f Setup\n\n**1. Retell AI Account**\n[Create a free account and get $10 in credits](https://www.retellai.com/?via=intellagents\n) (~73 mins of calls)\n\n\n**2. Add Your Credentials**\n- Retell AI API key \u2192 Bearer Auth credential\n- Google Sheets OAuth\n\n\n**3. Configure the \"Make phone call\" Node**\n- `from_number`: Your Retell phone number (with country code)\n- `to_number`: Change to `{{ $json['Phone Number'] }}`\n- `agent_id`: Your Retell agent ID\n\n\n**4. Connect Your Google Sheet**\nColumns needed: `Phone Number`, `Name`, `Status`\nSet new leads to Status = \"Not called\"\n\n### \ud83d\udcfa Video Walkthrough\n[COMING SOON]\n\n### \ud83d\udca1 Customisation Ideas\n- Swap the trigger for a webhook, form, or CRM\n- Adjust calling hours in the IF node (currently 8am-5pm)\n- Add more country codes to the timezone mapping\n- Send results to Slack, CRM, or email\n\n---\nBuilt by Marcus Taylor\n\ud83d\udcfa @intellagents | \ud83c\udf10 [voiceai.guide](https://voiceai.guide)"
      },
      "typeVersion": 1
    },
    {
      "id": "47bd5d82-dcec-4bc4-8209-25fadaf45254",
      "name": "Update Google Sheets with call information",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1696,
        1040
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "gid=0"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": ""
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    }
  ],
  "active": true,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "connections": {
    "Wait": {
      "main": [
        [
          {
            "node": "Get lead's timezone",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "New lead": {
      "main": [
        [
          {
            "node": "Cleanse phone numbers",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 1 minute": {
      "main": [
        [
          {
            "node": "Get Call Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Call Status": {
      "main": [
        [
          {
            "node": "Call has finished",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Make phone call": {
      "main": [
        [
          {
            "node": "Wait 1 minute",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Call has finished": {
      "main": [
        [
          {
            "node": "Update Google Sheets with call information",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait 1 minute",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get lead's timezone": {
      "main": [
        [
          {
            "node": "Is it between 8am-5pm for them?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Cleanse phone numbers": {
      "main": [
        [
          {
            "node": "Filter numbers we've already called",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is it between 8am-5pm for them?": {
      "main": [
        [
          {
            "node": "Make phone call",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter numbers we've already called": {
      "main": [
        [
          {
            "node": "Get lead's timezone",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This n8n workflow automates outbound phone calls to new leads using Retell AI, with built-in timezone detection to ensure you're only calling during business hours.

Source: https://n8n.io/workflows/12856/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

This n8n template demonstrates how to create a comprehensive marketing automation and booking system that combines Excel-based lead management with voice-powered customer interactions. The system util

Google Sheets Trigger, HTTP Request, Google Sheets +1
AI & RAG

This automation is designed to help you generate AI-powered music tracks, cover art, and fully rendered music videos — all triggered from a simple Telegram chat and managed via Google Sheets.

OpenAI Chat, Memory Buffer Window, Output Parser Structured +11
AI & RAG

This workflow is designed for marketers, content creators, agencies, and solo founders who want to publish long‑form posts with visuals on autopilot using n8n and AI agents. ​

Tool Http Request, Agent, HTTP Request +27
AI & RAG

WhatsApp AI Assistant for Clinic Appointment Booking Automate your entire appointment lifecycle with an intelligent AI assistant that lives on WhatsApp. This workflow empowers any clinic or independen

Google Gemini Chat, WhatsApp Trigger, Memory Buffer Window +9
AI & RAG

Categories: Business Automation, Content Creation, SEO, AI

Memory Buffer Window, Agent, n8n +6