AutomationFlowsAI & RAG › AI Appointment Setter Sheet

AI Appointment Setter Sheet

AI Appointment Setter Sheet. Uses googleSheetsTrigger, chainLlm, lmChatOpenAi, googleSheets. Event-driven trigger; 7 nodes.

Event trigger★★★★☆ complexityAI-powered7 nodesGoogle Sheets TriggerChain LlmOpenAI ChatGoogle Sheets
AI & RAG Trigger: Event Nodes: 7 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow follows the Chainllm → Google Sheets 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": "AI Appointment Setter Sheet",
  "nodes": [
    {
      "parameters": {
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "documentId": {
          "__rl": true,
          "value": "1jtFy5Ox0YgTfgUwVW2Bv0xFwbaZlq17946vyxPRdz8I",
          "mode": "list",
          "cachedResultName": "AI Appointment Setter",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1jtFy5Ox0YgTfgUwVW2Bv0xFwbaZlq17946vyxPRdz8I/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1jtFy5Ox0YgTfgUwVW2Bv0xFwbaZlq17946vyxPRdz8I/edit#gid=0"
        },
        "event": "rowAdded",
        "options": {}
      },
      "id": "a0793589-5f95-4b46-9ce2-3dcdff284315",
      "name": "Google Sheets Trigger",
      "type": "n8n-nodes-base.googleSheetsTrigger",
      "typeVersion": 1,
      "position": [
        -32,
        -16
      ],
      "credentials": {
        "googleSheetsTriggerOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "notes": "Polls the sheet every minute for newly added lead rows."
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "aps-field-001",
              "name": "fullName",
              "value": "={{ $json.Name || 'there' }}",
              "type": "string"
            },
            {
              "id": "aps-field-002",
              "name": "email",
              "value": "={{ $json.Email || '' }}",
              "type": "string"
            },
            {
              "id": "aps-field-003",
              "name": "company",
              "value": "={{ $json.Company || 'your company' }}",
              "type": "string"
            },
            {
              "id": "aps-field-004",
              "name": "industry",
              "value": "={{ $json.Industry || '' }}",
              "type": "string"
            },
            {
              "id": "aps-field-005",
              "name": "priority",
              "value": "={{ $json.Priority || 'Medium' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "a0729273-b768-4467-b11c-8b47e8923d8d",
      "name": "Normalize Row Data",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        144,
        -16
      ],
      "notes": "Maps raw spreadsheet columns into clean variables."
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "aps-sender-001",
              "name": "senderName",
              "value": "Alex Morgan",
              "type": "string"
            },
            {
              "id": "aps-sender-002",
              "name": "senderTitle",
              "value": "Automation Consultant",
              "type": "string"
            },
            {
              "id": "aps-sender-004",
              "name": "senderCompany",
              "value": "NextWave AI",
              "type": "string"
            },
            {
              "id": "aps-sender-005",
              "name": "senderEmail",
              "value": "hello@nextwave-ai-demo.example",
              "type": "string"
            },
            {
              "id": "172f5375-a6f2-48ad-be58-774606c8a39a",
              "name": "senderPhone",
              "value": "+1-555-0100",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "ae7d0279-0792-4dce-b876-e841b3c438bd",
      "name": "Sender Info (edit these values)",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        304,
        112
      ],
      "notes": "Fixed, deterministic sender identity. These values are NOT generated by AI \u2014 edit them once here with your real name, title, company, email, and phone. The signature is appended to every email exactly as written here, which is how this workflow guarantees no unfilled template brackets ever reach the sheet."
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Write ONLY the body of a short, warm, professional outreach email \u2014 no subject line, no greeting-only fluff, and absolutely NO signature or sign-off (that will be added separately). Do not include any placeholder brackets like [Your Name] or [Company] anywhere \u2014 you do not need to sign the email at all.\n\nWrite to:\nName: {{ $json.fullName }}\nCompany: {{ $json.company }}\nIndustry: {{ $json.industry }}\nPriority: {{ $json.priority }}\n\nThe email should reference their company and industry naturally, explain briefly why we're reaching out about AI automation services, and end with a soft call-to-action asking if they're open to a short call. Maximum 120 words. Return plain text only, no JSON, no markdown."
      },
      "id": "e427058b-ab60-4a45-8c76-554d5f739599",
      "name": "Generate Email Body",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.5,
      "position": [
        464,
        16
      ],
      "notes": "AI writes ONLY the body text. It never generates a signature, which is the direct fix for the earlier [Your Name]/[Your Company] placeholder bug."
    },
    {
      "parameters": {
        "options": {
          "maxTokens": 500
        }
      },
      "id": "d914ed77-a959-479c-b03d-5a2b3c0e2ca3",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1,
      "position": [
        560,
        208
      ],
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const aiItem = $input.first();\nconst normalized = $('Normalize Row Data').item.json;\nconst sender = $('Sender Info (edit these values)').item.json;\n\nlet body = '';\ntry {\n  body = (aiItem.json.text || aiItem.json.output || '').trim();\n} catch (e) {\n  body = '';\n}\n\n// Detect leftover template placeholders in the AI body\nfunction hasPlaceholder(value) {\n  return typeof value === 'string' && /\\[(your|company|email|phone|name|title)[^\\]]*\\]/i.test(value);\n}\n\nconst safeBody = (body.length > 10 && !hasPlaceholder(body))\n  ? body\n  : `Hi ${normalized.fullName}, we help companies like ${normalized.company} automate repetitive workflows with AI. Would you be open to a short call this week to see if it's a fit?`;\n\n// Build the deterministic signature \u2014 never AI-generated, so it can never contain unfilled brackets\nconst signature = `\\n\\nBest regards,\\n${sender.senderName}\\n${sender.senderTitle}\\n${sender.senderCompany}\\n${sender.senderEmail} | ${sender.senderPhone}`;\n\nconst subject = `Quick question about automation at ${normalized.company}`;\n\nconst finalEmail = `Subject: ${subject}\\n\\nHi ${normalized.fullName},\\n\\n${safeBody}${signature}`;\n\nreturn [{\n  json: {\n    email: normalized.email,\n    outreachEmail: finalEmail,\n    status: 'Ready to Send'\n  }\n}];"
      },
      "id": "1f13a04c-81da-425f-b1b9-c4e703c39780",
      "name": "Compose Final Email",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        736,
        16
      ],
      "notes": "Rejects any AI body still containing template brackets and falls back to a safe generic message. Always appends the deterministic signature from Sender Info \u2014 this is what guarantees no placeholder can ever reach the sheet."
    },
    {
      "parameters": {
        "operation": "appendOrUpdate",
        "documentId": {
          "__rl": true,
          "value": "1jtFy5Ox0YgTfgUwVW2Bv0xFwbaZlq17946vyxPRdz8I",
          "mode": "list",
          "cachedResultName": "AI Appointment Setter",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1jtFy5Ox0YgTfgUwVW2Bv0xFwbaZlq17946vyxPRdz8I/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Email": "={{ $json.email }}",
            "OutreachEmail": "={{ $json.outreachEmail }}",
            "Status": "={{ $json.status }}"
          },
          "matchingColumns": [
            "Email"
          ],
          "schema": [
            {
              "id": "Name",
              "displayName": "Name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Email",
              "displayName": "Email",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Company",
              "displayName": "Company",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Industry",
              "displayName": "Industry",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "Priority",
              "displayName": "Priority",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "OutreachEmail",
              "displayName": "OutreachEmail",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "displayName": "Status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "id": "449fc6ee-97d8-4de1-ac19-7a1ca3362b48",
      "name": "Append or Update Row",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        944,
        48
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "notes": "Writes the final composed email and status back into the matching row, identified by email."
    }
  ],
  "connections": {
    "Google Sheets Trigger": {
      "main": [
        [
          {
            "node": "Normalize Row Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Row Data": {
      "main": [
        [
          {
            "node": "Sender Info (edit these values)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Email Body",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Generate Email Body": {
      "main": [
        [
          {
            "node": "Compose Final Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compose Final Email": {
      "main": [
        [
          {
            "node": "Append or Update Row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sender Info (edit these values)": {
      "main": [
        [
          {
            "node": "Generate Email Body",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate"
  },
  "versionId": "b5ae25a7-ab05-43ea-b1c0-91b531d37942",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodeGroups": [],
  "id": "snCyZeZ1J9J1AW61",
  "tags": [
    {
      "name": "appointment-setter",
      "id": "uS2z2Tr4l5OBDIYc",
      "updatedAt": "2026-08-02T11:15:43.284Z",
      "createdAt": "2026-08-02T11:15:43.284Z"
    },
    {
      "updatedAt": "2026-07-25T18:18:08.707Z",
      "createdAt": "2026-07-25T18:18:08.707Z",
      "id": "9oczA1WlNB9PVRVX",
      "name": "google-sheets"
    },
    {
      "updatedAt": "2026-07-12T08:55:20.350Z",
      "createdAt": "2026-07-12T08:55:20.350Z",
      "id": "zLEptWcWohWystrd",
      "name": "openai"
    },
    {
      "updatedAt": "2026-07-19T15:39:41.698Z",
      "createdAt": "2026-07-19T15:39:41.698Z",
      "id": "RZhcdgrGYx5XyAIk",
      "name": "ai-automation"
    }
  ]
}

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

AI Appointment Setter Sheet. Uses googleSheetsTrigger, chainLlm, lmChatOpenAi, googleSheets. Event-driven trigger; 7 nodes.

Source: https://github.com/nextwave-ai/ai-automation-portfolio/blob/main/Projects/AI-Appointment-Setter-Sheet/Workflow.json — 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 end-to-end AI-powered recruitment automation workflow helps HR and talent acquisition teams automate the complete hiring pipeline—from resume intake and parsing to GPT-4-based evaluation, TA appr

Form Trigger, Output Parser Structured, Google Drive +10
AI & RAG

&gt; by Thomas Vie Thomas@pollup.net

HTTP Request, OpenAI Chat, Output Parser Structured +4
AI & RAG

Workflow 2823. Uses httpRequest, lmChatOpenAi, outputParserStructured, chainLlm. Event-driven trigger; 19 nodes.

HTTP Request, OpenAI Chat, Output Parser Structured +4
AI & RAG

2823. Uses httpRequest, lmChatOpenAi, outputParserStructured, chainLlm. Event-driven trigger; 19 nodes.

HTTP Request, OpenAI Chat, Output Parser Structured +4
AI & RAG

This workflow watches for new rows in Google Sheets, uses an OpenAI chat model to score and categorize lead intent with structured output, writes the results back to the same sheet, and posts only hig

Google Sheets Trigger, Chain Llm, OpenAI Chat +3