8
n8n 中文网amn8n.com

使用AI助手和智能SMS提醒(24/7)自动化WhatsApp预约

高级

这是一个Support, AI领域的自动化工作流,包含 25 个节点。主要使用 Code, Sms77, Switch, Webhook, HttpRequest 等节点,结合人工智能技术实现智能自动化。 使用GPT-4助手、Cal.com和SMS提醒自动化WhatsApp预约系统

前置要求
  • HTTP Webhook 端点(n8n 会自动生成)
  • 可能需要目标 API 的认证凭证
  • Google Sheets API 凭证
  • OpenAI API Key
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "IyhsZQh7TFAGsDqL",
  "meta": {
    "instanceId": "a2b23892dd6989fda7c1209b381f5850373a7d2b85609624d7c2b7a092671d44",
    "templateCredsSetupCompleted": true
  },
  "name": "使用 AI 助手和智能 SMS 提醒(24/7)自动化 WhatsApp 预约",
  "tags": [],
  "nodes": [
    {
      "id": "1d45c88a-5c71-42bb-9207-a7060a64030b",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -500,
        480
      ],
      "parameters": {
        "color": 4,
        "width": 1620,
        "height": 280,
        "content": "# 🟩 步骤 3 — 在预约前发送 SMS 提醒"
      },
      "typeVersion": 1
    },
    {
      "id": "1274c8bb-78fd-445f-b3bb-c1adedba3acc",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -500,
        -540
      ],
      "parameters": {
        "width": 1620,
        "height": 460,
        "content": "# 🟫 步骤 1 — 筛选用户并建议预约"
      },
      "typeVersion": 1
    },
    {
      "id": "bb147090-c21c-4b75-bfd7-92b1b61646f5",
      "name": "便签3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -500,
        -60
      ],
      "parameters": {
        "color": 3,
        "width": 1620,
        "height": 520,
        "content": "# 🟥 步骤 2 — 自动预约"
      },
      "typeVersion": 1
    },
    {
      "id": "3b2b3031-55a6-4c98-a6a5-75eb746b2c99",
      "name": "Webhook 触发器(WhatsApp 输入)",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -960,
        -80
      ],
      "webhookId": "6438cd95-74cb-4f40-a1a5-853706fe96f6",
      "parameters": {
        "path": "6438cd95-74cb-4f40-a1a5-853706fe96f6",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "6c7ffde6-b3bc-4162-b45d-8e483e347d49",
      "name": "切换:确认与聊天流程",
      "type": "n8n-nodes-base.switch",
      "position": [
        -740,
        -80
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Discussion",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "2b0aa9e5-7215-435b-8b66-fddb9973c7d0",
                    "operator": {
                      "type": "string",
                      "operation": "notContains"
                    },
                    "leftValue": "={{ $json.body.userInput }}",
                    "rightValue": "confirm"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Confirm booking",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "ed81f2e7-f97d-4581-bc84-ed703db2ec08",
                    "operator": {
                      "type": "string",
                      "operation": "contains"
                    },
                    "leftValue": "={{ $json.body.userInput }}",
                    "rightValue": "confirm"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "f7f43574-cfed-4448-983f-dc53343ec4ab",
      "name": "AI 预约助手(Dr Firas)",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        40,
        -460
      ],
      "parameters": {
        "text": "=input :  {{ $json.body.userInput }}",
        "options": {
          "systemMessage": "=#IMPORTANT  \nLes rendez-vous ne peuvent être pris qu’après avoir collecté le nom, l’e-mail/Courriel et le service choisi.\n\n#IDENTITÉ  \nVous êtes l’assistant de Dr Firas, coach expert en automatisation n8n, sur WhatsApp.\n\n#CONTEXTE  \n- Dr Firas propose des coachings et ateliers en ligne pour maîtriser n8n  \n- Horaires : du lundi au vendredi, 9 h–18 h (heure de Paris)  \n- Français et anglais possibles\n- La date d'aujourd'hui est : {{$now}}\n\n#SERVICES ET MAPPING (CRITIQUE)  \n- « Audit express n8n » (30 min) – 40 $ – Event ID : 2638115  \n- « Coaching Workflow n8n » (60 min) – 65 $ – Event ID : 2638127  \n- « Atelier Automatisation Avancée » (90 min) – 99 $ – Event ID : 2638131  \n\nQuand l’utilisateur choisit un service, mémorisez l’Event ID correspondant.\n\n#TON  \nDynamique, professionnel et chaleureux. Emojis légers 👍🤖.\n\n#FLUX DE CONVERSATION\n\n## 1. Accueil  \n« Bonjour ! Je suis l’assistant de Dr Firas, prêt à vous guider pour votre session n8n. Puis-je vous poser quelques questions ? »\n\n## 2. Choix du service (une question à la fois)  \na) « Quel service souhaitez-vous ? »  \n- Si « je ne sais pas » ou « que proposez-vous » → listez les 3 services avec durée, prix et bénéfice court.  \n- Sinon, continuez.\n\n## 3. Collecte des infos client  \n« Parfait ! Pour réserver votre [nom du service], j’ai besoin de votre prénom et de votre e-mail. »\n\n→ Après avoir reçu nom + e-mail/Courriel , enregistrez dans le Google Sheet Prospect (nom, e-mail, téléphone={{ $json.body.phoneNumber }}, service, Event ID, résumé).\n\n## 4. Proposition de créneaux  \n« Souhaitez-vous voir les disponibilités immédiatement ? »  \n- Si oui :  \n  1. Appelez l’outil **Get Availability** avec :  \n     - Event ID du service  \n     - startTime = maintenant (ISO 8601, UTC, ex. `2025-06-13T12:00:00Z`)  \n     - endTime = +48 h (ISO 8601, UTC)  \n     - max 5 créneaux  \n  2. Convertissez chaque horaire UTC en heure de Paris (+02:00) et affichez-les en plain text (ex. “14:30”).\n\n## 5. Choix du créneau  \n« Lequel de ces créneaux vous convient ? »  \n- L’utilisateur répond date+heure →  \n  - Demandez « Tapez “oui” pour confirmer ce créneau ».  \n  - À « oui », enregistrez via Google Sheet Update_Leads (nom, e-mail, service, Event ID, date+heure).  \n  - Puis : « Votre rendez-vous est fixé au [date] à [heure] (heure de Paris). Tapez “confirm” pour finaliser. »\n\n## 6. Confirmation finale  \n- À “confirm” →  \n  - Confirmez la réservation et rappelez la politique d’annulation 24 h à l’avance.  \n  - Proposez un lien d’ajout au calendrier si besoin.\n\n#CONTRAINTES OUTIL “Get Availability”  \n- Toujours ISO 8601 UTC  \n- startTime = maintenant, endTime = +48 h  \n- Maximum 5 créneaux  \n- Plain text, < 400 car.\n\n#RÈGLES GÉNÉRALES  \n- Une question à la fois  \n- Ne jamais redemander une info déjà fournie  \n- “RESET” relance la conversation depuis le début  \n"
        },
        "promptType": "define"
      },
      "typeVersion": 1.8
    },
    {
      "id": "03d391f5-43b5-462e-8c56-cdce426f9751",
      "name": "LLM:GPT-4o 聊天模型",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -200,
        -260
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o",
          "cachedResultName": "gpt-4o"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "id": "6h3DfVhNPw9I25nO",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "365fc08f-6514-4182-b77f-86a13ef55576",
      "name": "AI 对话记忆",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        -40,
        -260
      ],
      "parameters": {
        "sessionKey": "={{ $json.body.contactId }}",
        "sessionIdType": "customKey",
        "contextWindowLength": 50
      },
      "typeVersion": 1.3
    },
    {
      "id": "96eeb9b5-aea9-46cc-893f-4ea3697a3fb5",
      "name": "在 Google Sheets 中创建新潜在客户",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        140,
        -260
      ],
      "parameters": {
        "columns": {
          "value": {
            "Nom": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Nom', ``, 'string') }}",
            "Courriel": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Courriel', ``, 'string') }}",
            "Résumé": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('R_sum_', `ici il y a le résumé de toute la conversation`, 'string') }}",
            "ID du contact": "={{ $json.body.contactId }}",
            "Numéro de téléphone": "={{ $json.body.phoneNumber }}"
          },
          "schema": [
            {
              "id": "Nom",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Nom",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Courriel",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Courriel",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Numéro de téléphone",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Numéro de téléphone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Résumé",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Résumé",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Réservé",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Réservé",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Nom de l’événement",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Nom de l’événement",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ID de l’événement",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "ID de l’événement",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date du rendez-vous",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Date du rendez-vous",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Rappel SMS envoyé",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Rappel SMS envoyé",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ID du contact",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "ID du contact",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "ID du contact"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1PsuURJg5nxVnb18OMDShjC-sTA9i_32pyBSjfswOpqc/edit#gid=0",
          "cachedResultName": "mes RDV"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1PsuURJg5nxVnb18OMDShjC-sTA9i_32pyBSjfswOpqc",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1PsuURJg5nxVnb18OMDShjC-sTA9i_32pyBSjfswOpqc/edit?usp=drivesdk",
          "cachedResultName": "MES RDV"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "51us92xkOlrvArhV",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "9fcb6a32-34e7-4c5a-997a-cd0fe58f62cd",
      "name": "使用预约详情更新潜在客户",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        320,
        -260
      ],
      "parameters": {
        "columns": {
          "value": {
            "ID du contact": "={{ $json.body.contactId }}",
            "Date du rendez-vous": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Date_du_rendez-vous', ``, 'string') }}",
            "ID de l’événement": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('ID_de_l__v_nement', ``, 'string') }}",
            "Nom de l’événement": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Nom_de_l__v_nement', ``, 'string') }}"
          },
          "schema": [
            {
              "id": "Nom",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Nom",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Courriel",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Courriel",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Numéro de téléphone",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Numéro de téléphone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Résumé",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Résumé",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Réservé",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Réservé",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Nom de l’événement",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Nom de l’événement",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ID de l’événement",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "ID de l’événement",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date du rendez-vous",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Date du rendez-vous",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Rappel SMS envoyé",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Rappel SMS envoyé",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ID du contact",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "ID du contact",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "ID du contact"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1PsuURJg5nxVnb18OMDShjC-sTA9i_32pyBSjfswOpqc/edit#gid=0",
          "cachedResultName": "mes RDV"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1PsuURJg5nxVnb18OMDShjC-sTA9i_32pyBSjfswOpqc",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1PsuURJg5nxVnb18OMDShjC-sTA9i_32pyBSjfswOpqc/edit?usp=drivesdk",
          "cachedResultName": "MES RDV"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "51us92xkOlrvArhV",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "9e1a6470-0937-4190-9d24-259ea2749190",
      "name": "获取可用时间段",
      "type": "@n8n/n8n-nodes-langchain.toolHttpRequest",
      "position": [
        520,
        -260
      ],
      "parameters": {
        "url": "https://api.cal.com/v2/slots/available",
        "sendQuery": true,
        "sendHeaders": true,
        "parametersQuery": {
          "values": [
            {
              "name": "eventTypeId"
            },
            {
              "name": "startTime"
            },
            {
              "name": "endTime"
            }
          ]
        },
        "toolDescription": "Appelez cet outil pour récupérer la disponibilité des rendez-vous.\nVous devez impérativement utiliser le fuseau horaire de Paris.\nRespectez strictement le format ISO pour les dates, par exemple :\n2025-01-01T09:00:00-02:00\nExemple de schéma d’entrée :\n{\n  \"startTime\": \"...\",\n  \"endTime\": \"...\"\n}",
        "parametersHeaders": {
          "values": [
            {
              "name": "Authorization",
              "value": "Bearer cal_live_6cd60e462f8dc9b065fb48624e5918d8",
              "valueProvider": "fieldValue"
            },
            {
              "name": "Content-Type",
              "value": "application/json",
              "valueProvider": "fieldValue"
            }
          ]
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "5d8dc1ce-020f-4df3-88cc-7f61594a3347",
      "name": "发送响应到 WhatsApp",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        700,
        -460
      ],
      "parameters": {
        "options": {},
        "respondWith": "allIncomingItems"
      },
      "typeVersion": 1.1
    },
    {
      "id": "5cd00123-c0ea-48f9-85e6-e496a0bd6620",
      "name": "检索潜在客户详情",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -420,
        140
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $('Webhook Trigger (WhatsApp Input)').item.json.body.contactId }}",
              "lookupColumn": "ID du contact"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1PsuURJg5nxVnb18OMDShjC-sTA9i_32pyBSjfswOpqc/edit#gid=0",
          "cachedResultName": "mes RDV"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1PsuURJg5nxVnb18OMDShjC-sTA9i_32pyBSjfswOpqc",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1PsuURJg5nxVnb18OMDShjC-sTA9i_32pyBSjfswOpqc/edit?usp=drivesdk",
          "cachedResultName": "MES RDV"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "51us92xkOlrvArhV",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "9696140f-857f-42b6-9bb9-3d25f5d893d4",
      "name": "标准化预约时间(UTC 格式)",
      "type": "n8n-nodes-base.code",
      "position": [
        -200,
        140
      ],
      "parameters": {
        "jsCode": "const input = $input.first().json['Date du rendez-vous']; // e.g.,\n\"2025-06-04T09:00:00+02:00\"\nfunction normalizeToUTCZFormat(dateString) {\ntry {\nif\n(/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?Z$/.test(dateString)\n) {\nreturn new Date(dateString).toISOString();\n}\nconst date = new Date(dateString);\nif (isNaN(date.getTime())) {\nthrow new Error(\"Invalid date format\");\n}\nreturn date.toISOString(); // Returns in UTC with Z\n} catch (e) {\nreturn `Invalid input: ${e.message}`;\n}\n}\nreturn [\n{\njson: {\noriginal: input,\nnormalized: normalizeToUTCZFormat(input),\n},\n},\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "6d2ef19a-93d1-46f1-bc31-ad1010d3bb4a",
      "name": "发送预约",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        20,
        140
      ],
      "parameters": {
        "url": "https://api.cal.com/v2/bookings",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n\"eventTypeId\": {{ $('Retrieve Prospect Details').item.json['ID de l’événement'] }},\n\"start\": \"{{ $json.normalized }}\",\n\"attendee\": {\n\"name\": \"{{ $('Retrieve Prospect Details').item.json.Nom }}\",\n\"email\": \"{{ $('Retrieve Prospect Details').item.json.Courriel }}\",\n\"timeZone\": \"Europe/Paris\"\n},\n\"bookingFieldsResponses\": {\n\"title\": \"{{ $('Retrieve Prospect Details').item.json['Résumé'] }}\"\n}\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer cal_live_6cd60e462f8dc9b065fb48624e5918d8"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "cal-api-version",
              "value": "2024-08-13"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "472dc55f-b42f-483f-b6cc-1f8c25f0158e",
      "name": "格式化日期以便阅读(显示)",
      "type": "n8n-nodes-base.code",
      "position": [
        240,
        140
      ],
      "parameters": {
        "jsCode": "const input = $('Retrieve Prospect Details').first().json['Date du rendez-vous']; \nfunction formatToReadableDate(dateString) {\ntry {\nconst date = new Date(dateString);\nif (isNaN(date.getTime())) throw new Error(\"Invalid date\");\nconst options = {\ntimeZone: \"Europe/Berlin\", // Ensures correct local time\nmonth: \"long\",\nday: \"numeric\",\nhour: \"numeric\",\nminute: \"2-digit\",\nhour12: true,\n};\nconst formatted = date.toLocaleString(\"en-US\", options);\n// Example output: \"June 4, 09:00 AM\" → make it shorter like\n\"June 4, 9am\"\nreturn formatted\n.replace(\":00\", \"\") // remove :00 if exact hour\n.replace(\"AM\", \"am\")\n.replace(\"PM\", \"pm\");\n} catch (e) {\nreturn `Invalid input: ${e.message}`;\n}\n}\nreturn [\n{\njson: {\noriginal: input,\nformatted: formatToReadableDate(input),\n},\n},\n]; "
      },
      "typeVersion": 2
    },
    {
      "id": "7f0f86bc-8199-4d81-8bce-e96ced534f6e",
      "name": "检查预约状态(成功或错误)",
      "type": "n8n-nodes-base.switch",
      "position": [
        460,
        140
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "succeeded",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "cbf9b691-0bc1-4a14-b35d-96664d82bc91",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Send Booking').item.json.status }}",
                    "rightValue": "success"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Failed",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "f78214f9-ecd4-4913-aef0-9a2a453b052c",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $('Send Booking').item.json.status }}",
                    "rightValue": "error"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "108c9a6b-a345-4f33-ab22-113b6e63914b",
      "name": "Webhook 响应:预约已确认",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        700,
        -20
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "\n[\n{\n\"output\": \"Votre réservation a été effectuée avec succès. Vous recevrez bientôt un e-mail de confirmation pour votre massage, RDV : .\"\n}\n]"
      },
      "typeVersion": 1.1
    },
    {
      "id": "8c3d9734-7aad-4445-9810-29d7045fbc78",
      "name": "Webhook 响应:预约失败",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        700,
        280
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "[\n{\n\"output\": \"Il semble que le créneau horaire que vous avez choisi ne soit plus disponible. Veuillez en sélectionner un autre.\"\n}\n]"
      },
      "typeVersion": 1.1
    },
    {
      "id": "2a3ed659-5cec-497b-bde8-f6a15585cc41",
      "name": "在表格中标记预约为已确认",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        920,
        140
      ],
      "parameters": {
        "columns": {
          "value": {
            "Réservé": "confirm",
            "ID du contact": "={{ $('Retrieve Prospect Details').item.json['ID du contact'] }}"
          },
          "schema": [
            {
              "id": "Nom",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Nom",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Courriel",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Courriel",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Numéro de téléphone",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Numéro de téléphone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Résumé",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Résumé",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Réservé",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Réservé",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Nom de l’événement",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Nom de l’événement",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ID de l’événement",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "ID de l’événement",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date du rendez-vous",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Date du rendez-vous",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Rappel SMS envoyé",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Rappel SMS envoyé",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ID du contact",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "ID du contact",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "ID du contact"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1PsuURJg5nxVnb18OMDShjC-sTA9i_32pyBSjfswOpqc/edit#gid=0",
          "cachedResultName": "mes RDV"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1PsuURJg5nxVnb18OMDShjC-sTA9i_32pyBSjfswOpqc",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1PsuURJg5nxVnb18OMDShjC-sTA9i_32pyBSjfswOpqc/edit?usp=drivesdk",
          "cachedResultName": "MES RDV"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "51us92xkOlrvArhV",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "5bfa51a3-e939-41e8-a1df-b4e59c4feef1",
      "name": "触发器:每小时检查预约",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -420,
        580
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "0413b7b7-b523-4e22-ad4a-40d7342abed9",
      "name": "从表格中读取即将到来的预约",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -200,
        580
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1PsuURJg5nxVnb18OMDShjC-sTA9i_32pyBSjfswOpqc/edit#gid=0",
          "cachedResultName": "mes RDV"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1PsuURJg5nxVnb18OMDShjC-sTA9i_32pyBSjfswOpqc",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1PsuURJg5nxVnb18OMDShjC-sTA9i_32pyBSjfswOpqc/edit?usp=drivesdk",
          "cachedResultName": "MES RDV"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "51us92xkOlrvArhV",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "408e1330-29e4-4de7-b681-1bb372b5f841",
      "name": "筛选未来 2 小时内的预约",
      "type": "n8n-nodes-base.code",
      "position": [
        20,
        580
      ],
      "parameters": {
        "jsCode": "const nowParis = new Date(\n  new Date().toLocaleString(\"en-US\", { timeZone: \"Europe/Paris\" })\n);\nconst nowTimestamp = nowParis.getTime();\nconst twoHoursFromNow = nowTimestamp + 2 * 60 * 60 * 1000;\n\nreturn items\n  .filter(item => {\n    const booked = item.json[\"Réservé\"]?.toLowerCase() === \"confirm\";\n    const reminderSent = item.json[\"Rappel SMS envoyé\"]?.toLowerCase() === \"envoyer\";\n    const rawDate = item.json[\"Date du rendez-vous\"];\n    if (!booked || reminderSent || !rawDate) {\n      return false;\n    }\n\n    const appointmentTimestamp = Date.parse(rawDate);\n    return appointmentTimestamp > nowTimestamp &&\n           appointmentTimestamp <= twoHoursFromNow;\n  })\n  .map(item => {\n    const rawDate = item.json[\"Date du rendez-vous\"];\n    const date = new Date(rawDate);\n    if (isNaN(date.getTime())) {\n      item.json.appointmentDisplayTime = \"(Invalid date)\";\n      return item;\n    }\n\n    const options = {\n      timeZone: \"Europe/Paris\",\n      month: \"long\",\n      day: \"numeric\",\n      hour: \"numeric\",\n      minute: \"2-digit\",\n      hour12: true\n    };\n\n    const formatted = date.toLocaleString(\"en-US\", options)\n      .replace(\":00\", \"\")\n      .replace(\"AM\", \"AM\")\n      .replace(\"PM\", \"PM\");\n\n    item.json.appointmentDisplayTime = formatted;\n    return item;\n  });\n"
      },
      "typeVersion": 2
    },
    {
      "id": "0a9d6747-85d2-400e-a9fd-b5768ae4605c",
      "name": "发送 SMS 提醒",
      "type": "n8n-nodes-base.sms77",
      "position": [
        240,
        580
      ],
      "parameters": {
        "to": "={{ $('Read Upcoming Appointments from Sheet').item.json['Numéro de téléphone'] }}",
        "message": "=Bonjour {{ $json.Nom }}, ceci est un petit rappel : votre rendez-vous pour {{ $json['Nom de l’événement'] }} est prévu dans les 2 prochaines heures ",
        "options": {}
      },
      "credentials": {
        "sms77Api": {
          "id": "nC2L4tQyF03xEMAX",
          "name": "seven account"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3a01ec5e-57ea-43ac-8e59-97d66e85454f",
      "name": "在表格中标记 SMS 已发送",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        460,
        580
      ],
      "parameters": {
        "columns": {
          "value": {
            "ID du contact": "={{ $('Read Upcoming Appointments from Sheet').item.json['ID du contact'] }}",
            "Rappel SMS envoyé": "envoyer"
          },
          "schema": [
            {
              "id": "Nom",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Nom",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Courriel",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Courriel",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Numéro de téléphone",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Numéro de téléphone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Résumé",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Résumé",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Réservé",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Réservé",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Nom de l’événement",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Nom de l’événement",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ID de l’événement",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "ID de l’événement",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Date du rendez-vous",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "Date du rendez-vous",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Rappel SMS envoyé",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Rappel SMS envoyé",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ID du contact",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "ID du contact",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "ID du contact"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "="
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "="
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "51us92xkOlrvArhV",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.5
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "5c82e86c-e3b2-4b70-b686-a782edbaee55",
  "connections": {
    "Send Booking": {
      "main": [
        [
          {
            "node": "Format Date for Readability (Display)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send SMS Reminder": {
      "main": [
        [
          {
            "node": "Mark SMS as Sent in Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Conversation Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Booking Assistant (Dr Firas)",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "LLM: GPT-4o Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Booking Assistant (Dr Firas)",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve Prospect Details": {
      "main": [
        [
          {
            "node": "Normalize Booking Time (UTC Format)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Available Time Slots": {
      "ai_tool": [
        [
          {
            "node": "AI Booking Assistant (Dr Firas)",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Switch: Confirm vs Chat Flow": {
      "main": [
        [
          {
            "node": "AI Booking Assistant (Dr Firas)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Retrieve Prospect Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Booking Assistant (Dr Firas)": {
      "main": [
        [
          {
            "node": "Send Response to WhatsApp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Trigger (WhatsApp Input)": {
      "main": [
        [
          {
            "node": "Switch: Confirm vs Chat Flow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create New Prospect in Google Sheet": {
      "ai_tool": [
        [
          {
            "node": "AI Booking Assistant (Dr Firas)",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Booking Time (UTC Format)": {
      "main": [
        [
          {
            "node": "Send Booking",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Response: Booking Confirmed": {
      "main": [
        [
          {
            "node": "Mark Booking as Confirmed in Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Prospect with Booking Details": {
      "ai_tool": [
        [
          {
            "node": "AI Booking Assistant (Dr Firas)",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Format Date for Readability (Display)": {
      "main": [
        [
          {
            "node": "Check Booking Status (Success or Error)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Upcoming Appointments from Sheet": {
      "main": [
        [
          {
            "node": "Filter Appointments Within Next 2 Hours",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Trigger: Check Appointments Every Hour": {
      "main": [
        [
          {
            "node": "Read Upcoming Appointments from Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Booking Status (Success or Error)": {
      "main": [
        [
          {
            "node": "Webhook Response: Booking Confirmed",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Webhook Response: Booking Failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Appointments Within Next 2 Hours": {
      "main": [
        [
          {
            "node": "Send SMS Reminder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

高级 - 客户支持, 人工智能

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
高级
节点数量25
分类2
节点类型14
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

作者
Dr. Firas

Dr. Firas

@drfiras

Automation expert and certified trainer. I create hands-on courses to master automation with n8n. Contact me to access my exclusive training and start building powerful workflows today. 🚀

外部链接
在 n8n.io 查看

分享此工作流