8
n8n 中文网amn8n.com

未出席 / 错过演示跟进

高级

这是一个Lead Nurturing, AI Chatbot领域的自动化工作流,包含 36 个节点。主要使用 Code, Merge, Slack, Filter, Hubspot 等节点。 通过 Calendly、Zoom 和 AI 生成的跟进恢复错过的演示

前置要求
  • Slack Bot Token 或 Webhook URL
  • HubSpot API Key
  • HTTP Webhook 端点(n8n 会自动生成)
  • 可能需要目标 API 的认证凭证
  • OpenAI API Key
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "qC8SFIEjlyWY0hvC",
  "meta": {
    "instanceId": "42b2652ebb0a87755df4710a5630695eec8e35cb0ce04a63b0e25751b1f044f1",
    "templateCredsSetupCompleted": true
  },
  "name": "未出席 / 错过演示跟进",
  "tags": [],
  "nodes": [
    {
      "id": "8531ef8f-43d0-452c-98ea-4645a92e7e42",
      "name": "概述",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        224,
        1120
      ],
      "parameters": {
        "color": 3,
        "width": 396,
        "height": 356,
        "content": "## 📋 工作流概述"
      },
      "typeVersion": 1
    },
    {
      "id": "dbd246ba-1a3b-4a1b-b75e-80b8932dff66",
      "name": "设置说明",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        656,
        944
      ],
      "parameters": {
        "color": 5,
        "width": 384,
        "height": 296,
        "content": "## ⚙️ 一次性设置(运行一次)"
      },
      "typeVersion": 1
    },
    {
      "id": "b5f882b3-e2b2-4487-a802-f4d1b2836283",
      "name": "Zoom 验证指南",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1056,
        496
      ],
      "parameters": {
        "color": 4,
        "width": 420,
        "height": 360,
        "content": "## 🔴 关键:ZOOM 验证"
      },
      "typeVersion": 1
    },
    {
      "id": "c718c4c9-7f69-4e73-9497-191bfd067fa5",
      "name": "路径 1 说明",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        656,
        1248
      ],
      "parameters": {
        "color": 3,
        "width": 380,
        "height": 280,
        "content": "## 📥 路径 1:预订跟踪"
      },
      "typeVersion": 1
    },
    {
      "id": "c38cc125-7571-4a3d-9cd3-542dea207de2",
      "name": "路径 2 说明",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        656,
        1536
      ],
      "parameters": {
        "color": 3,
        "width": 384,
        "height": 332,
        "content": "## 🔍 路径 2:出席检查"
      },
      "typeVersion": 1
    },
    {
      "id": "f9bd45e4-c8ad-4fd8-95a1-705a614aa6a0",
      "name": "AI 配置",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2592,
        1184
      ],
      "parameters": {
        "color": 5,
        "width": 400,
        "height": 336,
        "content": "## 🤖 AI 个性化"
      },
      "typeVersion": 1
    },
    {
      "id": "35d77e58-2101-47cc-96f2-9f4c3c605388",
      "name": "跟进操作",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3536,
        1440
      ],
      "parameters": {
        "color": 6,
        "width": 420,
        "height": 340,
        "content": "## 📤 跟进操作"
      },
      "typeVersion": 1
    },
    {
      "id": "73ed0acb-4dba-4826-8400-3d3fd59432d7",
      "name": "数据库配置",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2176,
        1184
      ],
      "parameters": {
        "color": 5,
        "width": 400,
        "height": 340,
        "content": "## 💾 数据库设置"
      },
      "typeVersion": 1
    },
    {
      "id": "a3248c16-6d27-4fd8-854f-a86171b20d90",
      "name": "Zoom 认证设置",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1728,
        1808
      ],
      "parameters": {
        "color": 4,
        "width": 360,
        "height": 300,
        "content": "## 🔐 ZOOM OAUTH 设置"
      },
      "typeVersion": 1
    },
    {
      "id": "894619ce-a0d6-4847-a836-21288a8df5d7",
      "name": "设置检查清单",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        224,
        1488
      ],
      "parameters": {
        "color": 4,
        "width": 400,
        "height": 380,
        "content": "## ⚠️ 设置检查清单"
      },
      "typeVersion": 1
    },
    {
      "id": "e108da24-f19b-4417-83fd-9e83088c8b4d",
      "name": "Calendly 预订 Webhook",
      "type": "n8n-nodes-base.webhook",
      "notes": "PATH 1: Receives booking data when a Calendly event is scheduled",
      "position": [
        1104,
        1312
      ],
      "webhookId": "46e46b56-06e4-4cb1-9b96-5dd9c628a2fa",
      "parameters": {
        "path": "cal-uri-get",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2.1
    },
    {
      "id": "bffa0474-3d78-41e7-b1c5-93967644c0dc",
      "name": "提取预订数据",
      "type": "n8n-nodes-base.code",
      "notes": "Parses Calendly webhook payload and extracts Zoom meeting details",
      "position": [
        1328,
        1312
      ],
      "parameters": {
        "jsCode": "// Extract all booking data from Calendly webhook\nconst payload = $json.body.payload;\nconst scheduledEvent = payload.scheduled_event;\nconst location = scheduledEvent.location;\n\n// Extract Zoom meeting ID\nconst zoomMeetingId = location?.data?.id || null;\nconst zoomJoinUrl = location?.join_url || null;\n\nif (!zoomMeetingId) {\n  throw new Error('No Zoom meeting ID found - is this a Zoom meeting?');\n}\n\nreturn [{\n  json: {\n    // IDs\n    booking_id: payload.uri,\n    meeting_id: parseInt(zoomMeetingId),\n    event_type_uri: scheduledEvent.event_type,\n    \n    // Meeting details\n    zoom_join_url: zoomJoinUrl,\n    zoom_password: location?.data?.password || null,\n    event_name: scheduledEvent.name,\n    start_time: scheduledEvent.start_time,\n    end_time: scheduledEvent.end_time,\n    \n    // Attendee info\n    email: payload.email,\n    name: payload.name,\n    \n    // Tracking\n    status: 'pending',\n    created_at: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "f2ad5409-75a3-4dc2-a439-416b01d3d62c",
      "name": "筛选:仅演示事件",
      "type": "n8n-nodes-base.filter",
      "notes": "⚠️ CONFIGURE: Replace 'YOUR_CALENDLY_EVENT_TYPE_URI' with your specific demo event type URI from Calendly",
      "position": [
        1552,
        1312
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "f41e5129-f6e0-414a-9e38-1735f3da3b1d",
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $json.event_type_uri }}",
              "rightValue": "YOUR_CALENDLY_EVENT_TYPE_URI"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "3cec4aa0-a335-4dc3-85e9-dd62a336dfcc",
      "name": "Zoom 会议结束 Webhook",
      "type": "n8n-nodes-base.webhook",
      "notes": "PATH 2: Receives webhook when Zoom meeting ends to check attendance",
      "position": [
        1104,
        1568
      ],
      "webhookId": "50fd0f57-cb75-4640-8b39-23c46cff060c",
      "parameters": {
        "path": "zoom-meeting-ended",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2.1
    },
    {
      "id": "9934e2fc-53b5-4811-a1c7-97eaf985e6c6",
      "name": "筛选:会议结束事件",
      "type": "n8n-nodes-base.filter",
      "notes": "Ensures we only process meeting.ended events from Zoom",
      "position": [
        1488,
        1568
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "string": [
            {
              "value1": "={{ $json.body.event }}",
              "value2": "meeting.ended",
              "operation": "equals"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "16831094-cc44-490d-8747-bb5d322486fb",
      "name": "从 Zoom 提取会议 ID",
      "type": "n8n-nodes-base.code",
      "notes": "Parses Zoom webhook to get meeting ID for lookup",
      "position": [
        1664,
        1568
      ],
      "parameters": {
        "jsCode": "// Extract Zoom meeting ID from webhook and convert to number\nconst meetingId = parseInt($json.body?.payload?.object?.id);\n\nif (!meetingId) {\n  throw new Error('No meeting ID in Zoom webhook');\n}\n\nreturn [{\n  json: {\n    meeting_id: meetingId,\n    meeting_uuid: $json.body?.payload?.object?.uuid,\n    ended_at: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "1b53510d-2ec2-4528-bf68-00be8314c828",
      "name": "获取 Zoom 参与者",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "Fetches list of participants from Zoom API to check who attended",
      "position": [
        2208,
        1568
      ],
      "parameters": {
        "url": "=https://api.zoom.us/v2/past_meetings/{{ $json.meeting_id }}/participants",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $('Get Zoom Access Token').item.json.access_token }}"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "984a024a-9421-4350-b05b-3f829dc7ebc1",
      "name": "检查与会者是否出席",
      "type": "n8n-nodes-base.code",
      "notes": "Compares expected attendee email with actual Zoom participants",
      "position": [
        2384,
        1568
      ],
      "parameters": {
        "jsCode": "// Check if expected attendee actually joined\nconst bookingData = $('Get Booking from Database').first().json;\nconst expectedEmail = bookingData.email.toLowerCase();\nconst participants = $json.participants || [];\n\nconst attended = participants.some(p => \n  p.user_email && p.user_email.toLowerCase() === expectedEmail\n);\n\nreturn [{\n  json: {\n    ...bookingData,\n    attended: attended,\n    participant_count: participants.length,\n    participants_list: participants.map(p => p.name || p.user_email).join(', ')\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "151f697a-1f1e-49c2-a691-01b45d1b9ddf",
      "name": "AI 生成跟进消息",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "notes": "⚠️ CONFIGURE: Connect your OpenAI API credentials. Customize this prompt to match your brand voice and follow-up style.",
      "position": [
        2832,
        1568
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "=Generate follow-up messages for a demo no-show.\n\nContext:\n- Email: {{ $json.email }}\n- Meeting ID: {{ $json.meeting_id }}\n- Participant count: {{ $json.participant_count }}\n- Who was there: {{ $json.participants_list }}\n- Attendee status: Did not attend\n\nCreate professional, empathetic follow-up messages:\n\n1. EMAIL SUBJECT (short, friendly, non-pushy)\n2. EMAIL BODY (3-4 sentences maximum - acknowledge the miss, express understanding that things happen, offer to reschedule OR send a quick Loom recording)\n3. LINKEDIN MESSAGE (2 sentences, casual and brief)\n\nReturn ONLY valid JSON in this exact format:\n{\n  \"email_subject\": \"...\",\n  \"email_body\": \"...\",\n  \"linkedin_message\": \"...\"\n}"
            }
          ]
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "4667b279-40bd-4246-80d3-7b5d74cf639f",
      "name": "发送恢复邮件",
      "type": "n8n-nodes-base.emailSend",
      "notes": "⚠️ CONFIGURE: Replace 'YOUR_SALES_EMAIL@company.com' with your sender email. Configure your email provider credentials. Enable this node when ready to send automated emails.",
      "disabled": true,
      "position": [
        3344,
        1392
      ],
      "webhookId": "a7eaf7b8-4866-475a-b17a-53cc279665ff",
      "parameters": {
        "options": {},
        "subject": "={{ $json.email_subject }}",
        "toEmail": "={{ $json.email }}",
        "fromEmail": "YOUR_SALES_EMAIL@company.com"
      },
      "typeVersion": 2.1
    },
    {
      "id": "ec561207-12f7-4327-8e1b-fa6e191f4cc4",
      "name": "在 Slack 中通知团队",
      "type": "n8n-nodes-base.slack",
      "notes": "⚠️ CONFIGURE: 1) Connect Slack OAuth credentials 2) Select your channel 3) Customize the message template to include info your team needs (attendee name, company, demo type, next steps, etc.)",
      "position": [
        3344,
        1760
      ],
      "webhookId": "f4fd4148-0618-49f0-b3c4-55eba1454b31",
      "parameters": {
        "text": "=🎯 *NEW DEMO NO-SHOW*\n\n📧 Email: {{ $json.email }}\n🆔 Meeting ID: {{ $json.meeting_id }}\n👥 Participants: {{ $json.participants_list }}\n📊 Total in meeting: {{ $json.participant_count }}\n\n✅ *Actions Taken:*\n- Recovery email queued\n- Database updated\n\n💬 *Suggested Follow-ups:*\n{{ $json.linkedin_message }}\n\n---\nCustomize this message template to fit your team's needs!",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SLACK_CHANNEL_ID",
          "cachedResultName": "your-channel-name"
        },
        "otherOptions": {
          "mrkdwn": true
        },
        "authentication": "oAuth2"
      },
      "typeVersion": 2.3
    },
    {
      "id": "454864d6-90ec-4741-9f57-2cf86f0c8b33",
      "name": "保存预订到数据库",
      "type": "n8n-nodes-base.dataTable",
      "notes": "⚠️ CONFIGURE: Create a Data Table in n8n with columns: meeting_id, email, status. Link it here.",
      "position": [
        1744,
        1312
      ],
      "parameters": {
        "columns": {
          "value": {
            "email": "={{ $json.email }}",
            "meeting_id": "={{ $json.meeting_id }}"
          },
          "schema": [
            {
              "id": "meeting_id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "meeting_id",
              "defaultMatch": false
            },
            {
              "id": "email",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "email",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_DATA_TABLE_ID",
          "cachedResultUrl": "/projects/YOUR_PROJECT_ID/datatables/YOUR_DATA_TABLE_ID",
          "cachedResultName": "Calendly Events"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "32d50735-236a-4f69-b5a9-39d772d46965",
      "name": "从数据库获取预订",
      "type": "n8n-nodes-base.dataTable",
      "notes": "Retrieves the original booking details using Zoom meeting ID",
      "position": [
        1856,
        1648
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "meeting_id",
              "keyValue": "={{ $json.meeting_id }}"
            }
          ]
        },
        "matchType": "allConditions",
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_DATA_TABLE_ID",
          "cachedResultUrl": "/projects/YOUR_PROJECT_ID/datatables/YOUR_DATA_TABLE_ID",
          "cachedResultName": "Calendly Events"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "9a42b893-b4c0-4eaf-ad40-b3adf4799661",
      "name": "获取 Zoom 访问令牌",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "⚠️ CONFIGURE: Replace with your Zoom Server-to-Server OAuth credentials (Account ID, Client ID, Client Secret). Get these from your Zoom App in the Zoom Marketplace.",
      "position": [
        1856,
        1488
      ],
      "parameters": {
        "url": "https://zoom.us/oauth/token",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "grant_type",
              "value": "account_credentials"
            },
            {
              "name": "account_id",
              "value": "YOUR_ZOOM_ACCOUNT_ID"
            },
            {
              "name": "client_id",
              "value": "YOUR_ZOOM_CLIENT_ID"
            },
            {
              "name": "client_secret",
              "value": "YOUR_ZOOM_CLIENT_SECRET"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "40feb37d-85bb-4c18-9435-30d108b359e6",
      "name": "合并访问令牌与预订数据",
      "type": "n8n-nodes-base.merge",
      "notes": "Combines Zoom access token with meeting data for API call",
      "position": [
        2048,
        1568
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3.2
    },
    {
      "id": "4d9ecb8a-a6c7-4f54-ae84-f3dd981ae9ff",
      "name": "更新出席状态",
      "type": "n8n-nodes-base.dataTable",
      "notes": "Updates database with attendance status (true/false)",
      "position": [
        2640,
        1728
      ],
      "parameters": {
        "columns": {
          "value": {
            "email": "={{ $json.email}}",
            "status": "={{ $json.attended }}",
            "meeting_id": "={{ $json.meeting_id }}"
          },
          "schema": [
            {
              "id": "meeting_id",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "meeting_id",
              "defaultMatch": false
            },
            {
              "id": "email",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "email",
              "defaultMatch": false
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "status",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "filters": {
          "conditions": [
            {
              "keyName": "meeting_id",
              "keyValue": "={{ $json.meeting_id }}"
            }
          ]
        },
        "options": {},
        "operation": "update",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_DATA_TABLE_ID",
          "cachedResultUrl": "/projects/YOUR_PROJECT_ID/datatables/YOUR_DATA_TABLE_ID",
          "cachedResultName": "Calendly Events"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "49e2fe76-6b3b-4d73-895e-fadcbb515c45",
      "name": "更新 CRM 交易(可选)",
      "type": "n8n-nodes-base.hubspot",
      "notes": "⚠️ OPTIONAL: Enable and configure this to update HubSpot (or your CRM) deals when someone no-shows. Map the deal ID and fields you want to update.",
      "disabled": true,
      "position": [
        3344,
        1568
      ],
      "parameters": {
        "dealId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "resource": "deal",
        "operation": "update",
        "updateFields": {}
      },
      "typeVersion": 2.2
    },
    {
      "id": "a993d193-02c7-4c15-bf85-49ac4ce3132a",
      "name": "手动设置触发器",
      "type": "n8n-nodes-base.manualTrigger",
      "notes": "SETUP PATH: Run this once to create Calendly webhook subscription",
      "position": [
        1104,
        1088
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "33dbaafd-198b-4533-82f0-54920bac9f08",
      "name": "获取 Calendly 组织",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "⚠️ CONFIGURE: Replace 'YOUR_CALENDLY_API_TOKEN' with your Calendly Personal Access Token from https://calendly.com/integrations/api_webhooks",
      "position": [
        1328,
        1088
      ],
      "parameters": {
        "url": "https://api.calendly.com/users/me",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_TOKEN_HERE"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "1cfed318-50e7-4bed-b894-3ef5a7760360",
      "name": "提取组织 URI",
      "type": "n8n-nodes-base.code",
      "notes": "Parses Calendly response to get your organization URI",
      "position": [
        1552,
        1088
      ],
      "parameters": {
        "jsCode": "// Extract organization URI\nconst response = $input.first().json;\nconst orgUri = response.resource.current_organization;\n\nif (!orgUri) {\n  throw new Error('No organization found. You might be on a personal account.');\n}\n\nconsole.log('✅ Your Organization URI:', orgUri);\n\nreturn [{\n  json: {\n    organization_uri: orgUri,\n    user_uri: response.resource.uri,\n    user_name: response.resource.name,\n    user_email: response.resource.email\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "7c50f2e3-7f00-4cac-a890-61df07e935ef",
      "name": "创建 Calendly Webhook",
      "type": "n8n-nodes-base.httpRequest",
      "notes": "⚠️ CONFIGURE: Replace 'YOUR_N8N_WEBHOOK_URL' with your n8n instance URL and 'YOUR_CALENDLY_API_TOKEN' with your Calendly token",
      "position": [
        1776,
        1088
      ],
      "parameters": {
        "url": "https://api.calendly.com/webhook_subscriptions",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"url\": \"YOUR_N8N_WEBHOOK_URL/webhook/cal-uri-get\",\n  \"events\": [\"invitee.created\"],\n  \"organization\": \"{{ $json.organization_uri }}\",\n  \"scope\": \"organization\",\n  \"signing_key\": \"your_optional_secret_key\"\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_TOKEN_HERE"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.1
    },
    {
      "id": "7b694aed-9d5c-4321-90be-84d05cb33c25",
      "name": "显示设置成功",
      "type": "n8n-nodes-base.code",
      "position": [
        1984,
        1088
      ],
      "parameters": {
        "jsCode": "// Parse webhook creation response\nconst response = $input.first().json;\n\nif (response.resource) {\n  console.log('✅ WEBHOOK CREATED SUCCESSFULLY!');\n  console.log('Webhook URI:', response.resource.uri);\n  console.log('Webhook URL:', response.resource.url);\n  console.log('Events:', response.resource.events);\n  console.log('State:', response.resource.state);\n  \n  return [{\n    json: {\n      success: true,\n      webhook_uri: response.resource.uri,\n      webhook_url: response.resource.url,\n      events: response.resource.events,\n      state: response.resource.state,\n      message: '✅ Webhook subscription created! Calendly will now POST to your n8n webhook when bookings happen.'\n    }\n  }];\n} else {\n  throw new Error('Webhook creation failed: ' + JSON.stringify(response));\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "171e6449-1352-467b-8392-dbccf290c867",
      "name": "Zoom Webhook 验证器",
      "type": "n8n-nodes-base.code",
      "notes": "⚠️ CONFIGURE: Replace 'YOUR_ZOOM_WEBHOOK_SECRET' with the secret token from your Zoom app's Feature page",
      "position": [
        1328,
        896
      ],
      "parameters": {
        "jsCode": "const crypto = require('crypto');\n\n// Your Zoom webhook secret token\nconst ZOOM_WEBHOOK_SECRET = 'YOUR_ZOOM_WEBHOOK_SECRET';\n\nconst body = $json.body;\n\n// Check if this is a validation request\nif (body?.event === 'endpoint.url_validation') {\n  const plainToken = body.payload.plainToken;\n  \n  // Hash the plainToken using HMAC SHA-256\n  const encryptedToken = crypto\n    .createHmac('sha256', ZOOM_WEBHOOK_SECRET)\n    .update(plainToken)\n    .digest('hex');\n  \n  return [{\n    plainToken: plainToken,\n    encryptedToken: encryptedToken\n  }];\n}\n\n// For normal webhook events, pass through\nreturn [$json];"
      },
      "typeVersion": 2
    },
    {
      "id": "fbb4c54e-4fb6-41cc-98b8-8f5944cb508c",
      "name": "向 Zoom 发送验证响应",
      "type": "n8n-nodes-base.respondToWebhook",
      "notes": "Responds to Zoom's validation challenge with encrypted token",
      "position": [
        1520,
        896
      ],
      "parameters": {
        "options": {
          "responseCode": 200,
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              }
            ]
          }
        }
      },
      "typeVersion": 1.4
    },
    {
      "id": "33cf8321-30bb-49d6-bdd1-3463cb8ae23c",
      "name": "筛选:仅未出席",
      "type": "n8n-nodes-base.filter",
      "notes": "Only processes meetings where attendee did NOT show up (attended = false)",
      "position": [
        2624,
        1568
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "356a49d4-56b0-43f3-b640-f6e1c2f2b165",
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              },
              "leftValue": "={{ $json.attended }}",
              "rightValue": "0"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a367fc8c-3e58-4c9e-b5b0-0e12786e9987",
      "name": "Zoom 验证 Webhook(重复)",
      "type": "n8n-nodes-base.webhook",
      "notes": "DISABLED: This is a duplicate for validation testing. Delete after Zoom webhook is validated.",
      "disabled": true,
      "position": [
        1104,
        896
      ],
      "webhookId": "50fd0f57-cb75-4640-8b39-23c46cff060c",
      "parameters": {
        "path": "zoom-meeting-ended",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "0bff6195-14a8-43cf-9d02-871cf8944ab3",
  "connections": {
    "Extract Booking Data": {
      "main": [
        [
          {
            "node": "Filter: Demo Events Only",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Setup Trigger": {
      "main": [
        [
          {
            "node": "Get Calendly Organization",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter: No-Shows Only": {
      "main": [
        [
          {
            "node": "AI Generate Follow-Up Messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Zoom Access Token": {
      "main": [
        [
          {
            "node": "Merge Access Token with Booking Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Zoom Participants": {
      "main": [
        [
          {
            "node": "Check if Attendee Showed Up",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Zoom Webhook Validator": {
      "main": [
        [
          {
            "node": "Send Validation Response to Zoom",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Calendly Webhook": {
      "main": [
        [
          {
            "node": "Show Setup Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calendly Booking Webhook": {
      "main": [
        [
          {
            "node": "Extract Booking Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Organization URI": {
      "main": [
        [
          {
            "node": "Create Calendly Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter: Demo Events Only": {
      "main": [
        [
          {
            "node": "Save Booking to Database",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Booking from Database": {
      "main": [
        [
          {
            "node": "Merge Access Token with Booking Data",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Get Calendly Organization": {
      "main": [
        [
          {
            "node": "Extract Organization URI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Zoom Meeting Ended Webhook": {
      "main": [
        [
          {
            "node": "Filter: Meeting Ended Events",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if Attendee Showed Up": {
      "main": [
        [
          {
            "node": "Update Attendance Status",
            "type": "main",
            "index": 0
          },
          {
            "node": "Filter: No-Shows Only",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Meeting ID from Zoom": {
      "main": [
        [
          {
            "node": "Get Zoom Access Token",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Booking from Database",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter: Meeting Ended Events": {
      "main": [
        [
          {
            "node": "Extract Meeting ID from Zoom",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Generate Follow-Up Messages": {
      "main": [
        [
          {
            "node": "Notify Team in Slack",
            "type": "main",
            "index": 0
          },
          {
            "node": "Update CRM Deal (Optional)",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Recovery Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Zoom Validation Webhook (Duplicate)": {
      "main": [
        [
          {
            "node": "Zoom Webhook Validator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Access Token with Booking Data": {
      "main": [
        [
          {
            "node": "Get Zoom Participants",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

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

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

高级 - 客户培育, AI 聊天机器人

需要付费吗?

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

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

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

作者
Connor Provines

Connor Provines

@connorprovines

Over the past 10+ years, I've led marketing initiatives for B2B SaaS companies at every stage, from initial market entry (0→1) through scaled growth (1→10). My approach is grounded in a simple principle: strategic thinking paired with hands-on execution. I focus on building sustainable growth engines, not just campaigns. Whether it's positioning a new product, optimizing conversion funnels, or scaling demand generation, I bring both the framework and the tactical expertise to move fast and win.

外部链接
在 n8n.io 查看

分享此工作流