基于HubSpot上下文的AI邮件回复与Slack审批
高级
这是一个CRM, AI Summarization领域的自动化工作流,包含 19 个节点。主要使用 If, Code, Gmail, Slack, Filter 等节点。 基于HubSpot上下文的AI邮件回复与Slack审批
前置要求
- •Google 账号和 Gmail API 凭证
- •Slack Bot Token 或 Webhook URL
- •HubSpot API Key
- •可能需要目标 API 的认证凭证
- •Google Gemini API Key
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"meta": {
"instanceId": "09423a3357ff64bdcc082268b9d577001317edbe377a3ccfb0b131ffb9554b30"
},
"nodes": [
{
"id": "6897a614-cd5a-4e76-99fb-7094b4692dd2",
"name": "Google Gemini 聊天模型",
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"position": [
2352,
464
],
"parameters": {
"options": {}
},
"typeVersion": 1
},
{
"id": "1b011a66-c09a-4a6c-b666-89a5301f54cc",
"name": "回复消息",
"type": "n8n-nodes-base.gmail",
"position": [
3184,
240
],
"webhookId": "597ba693-ad1e-4a19-9c41-8f8a82e7849f",
"parameters": {
"message": "={{ $('Draft Reply (AI Agent)').item.json.output }}",
"options": {
"appendAttribution": false
},
"emailType": "text",
"messageId": "={{ $('Watch Gmail (New Inbound)').first().json.threadId }}",
"operation": "reply"
},
"typeVersion": 2.1
},
{
"id": "287d6e80-9e28-4188-87a8-c46def152c1e",
"name": "监控 Gmail(新进邮件)",
"type": "n8n-nodes-base.gmailTrigger",
"position": [
544,
256
],
"parameters": {
"filters": {},
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
}
},
"typeVersion": 1.3
},
{
"id": "4b25d17f-ce32-47d2-a27d-ca52c68a5e46",
"name": "过滤器:允许的发件人",
"type": "n8n-nodes-base.filter",
"position": [
752,
256
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "c0511cfb-d540-4b31-b665-f6038d6f8bbe",
"operator": {
"type": "string",
"operation": "notContains"
},
"leftValue": "={{ $json.From }}",
"rightValue": "n8n.io"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "61b5b40e-988e-4482-b729-0669e9081fb2",
"name": "起草回复(AI 代理)",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
2352,
256
],
"parameters": {
"text": "=You are a helpful, concise customer support/sales assistant. Draft a ready-to-send email reply.\n\nDO NOT output JSON, arrays, or anything under CONTEXT. Only output the email.\n\n# INPUTS\n\nMy name (for signature): John Bolton\nFrom: {{$('Watch Gmail (New Inbound)').first().json.From}}\nSubject: {{$('Watch Gmail (New Inbound)').first().json.Subject}}\nCustomer message:\n{{$('Watch Gmail (New Inbound)').first().json.snippet}}\n\n# CONTEXT (do not quote or restate; summarize only if helpful)\nContact (HubSpot JSON):\n{{ JSON.stringify($('Find Contact by Email').first().json.properties || {}, null, 2) }}\n\nCompanies (JSON, may be empty):\n{{ JSON.stringify($json.companies || []) }}\n\nDeals (JSON, may be empty):\n{{ JSON.stringify($json.deals || []) }}\n\nTickets (JSON, may be empty):\n{{ JSON.stringify($json.tickets || []) }}\n\n# WHAT TO DO\n- Acknowledge the sender and the exact topic in the Subject/body.\n- Answer their request directly and succinctly.\n- Offer 1–2 clear next steps or a single CTA.\n- Personalize using safe context only:\n - Use contact name/company if present.\n - If deals exist, mention at most the 1–2 most relevant (name, stage, amount, close date). Ignore IDs/owner/pipeline/internal fields.\n - If tickets exist, reference subject/status briefly if relevant.\n- If context is missing, write a generic but professional reply (do not invent facts).\n\n# TONE\nFriendly, professional, plain language. Short paragraphs or brief bullets.\n\n# OUTPUT FORMAT (no extra commentary, no subject, just the email body)\n- Greeting with the person’s name if available.\n- 2–5 sentences answering the question; bullets allowed for steps.\n- Optional one-line context (deal/ticket) if helpful.\n- One clear CTA.\n- Polite sign-off with a sender name placeholder.\n\n# CONSTRAINTS\n- Never expose IDs, raw JSON, or internal property names.\n- Keep under ~150 words unless necessary.\n- If anything is unclear, end with exactly one clarifying question.\n\nGenerate the reply now.\n",
"options": {},
"promptType": "define"
},
"typeVersion": 2.2
},
{
"id": "81616dff-2202-4e66-9c2f-7e93403bf909",
"name": "通过邮箱查找联系人",
"type": "n8n-nodes-base.hubspot",
"position": [
1040,
256
],
"parameters": {
"operation": "search",
"authentication": "oAuth2",
"filterGroupsUi": {
"filterGroupsValues": [
{
"filtersUi": {
"filterValues": [
{
"value": "={{ String($json.From || '').match(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}/i)?.[0] || '' }}",
"propertyName": "email|string"
}
]
}
}
]
},
"additionalFields": {
"properties": [
"email",
"firstname",
"lastname",
"jobtitle",
"company",
"country",
"state",
"city",
"hs_language",
"phone",
"mobilephone",
"lifecyclestage",
"hs_lead_status",
"hubspot_owner_id",
"hs_email_last_open_date",
"hs_email_last_reply_date",
"hs_latest_meeting_activity",
"hs_sequences_is_enrolled",
"hs_sequences_enrolled_count",
"createdate",
"hs_lastmodifieddate",
"hs_timezone",
"notes_last_contacted",
"hs_object_id"
]
}
},
"typeVersion": 2.1
},
{
"id": "6e5e8866-223c-4cdc-b201-7e804d47b01d",
"name": "设置记录类型",
"type": "n8n-nodes-base.code",
"position": [
1248,
256
],
"parameters": {
"jsCode": "const input = $input.first();\nlet records = Array.isArray(input?.json?.records)\n ? input.json.records\n : [\"deals\",\"companies\",\"tickets\"];\n\nreturn records.map(name => ({ json: { record: name } }));"
},
"typeVersion": 2
},
{
"id": "94281ea8-a451-42a9-9fb6-b90e4b5dc42a",
"name": "列出联系人关联",
"type": "n8n-nodes-base.httpRequest",
"position": [
1456,
256
],
"parameters": {
"url": "=https://api.hubapi.com/crm/v4/objects/contacts/{{ $('Find Contact by Email').item.json.id }}/associations/{{ $json.record }}",
"options": {
"response": {}
},
"authentication": "predefinedCredentialType",
"nodeCredentialType": "hubspotOAuth2Api"
},
"typeVersion": 4.2
},
{
"id": "70d7c8e3-bbc5-4be2-bc65-c5c168bcba84",
"name": "构建批量读取请求",
"type": "n8n-nodes-base.code",
"position": [
1664,
256
],
"parameters": {
"jsCode": "// Build batch/read requests for only: deals, companies, tickets\n\nconst PROPS = {\n deals: [\n \"dealname\",\n \"amount\",\n \"dealstage\",\n \"pipeline\",\n \"closedate\",\n \"hubspot_owner_id\",\n \"hs_lastmodifieddate\",\n ],\n companies: [\n \"name\",\n \"domain\",\n \"industry\",\n \"numberofemployees\",\n \"annualrevenue\",\n \"website\",\n \"phone\",\n \"city\",\n \"state\",\n \"country\",\n \"hubspot_owner_id\",\n \"createdate\",\n \"hs_lastmodifieddate\",\n ],\n tickets: [\n \"hs_ticket_id\",\n \"subject\",\n \"content\",\n \"hs_pipeline\",\n \"hs_pipeline_stage\",\n \"hs_ticket_priority\",\n \"hs_lastmodifieddate\",\n \"createdate\",\n \"closed_date\",\n ],\n};\n\n// If the upstream node emits these three in order, this helps infer the object when not provided\nconst ORDER = [\"deals\", \"companies\", \"tickets\"];\n\nfunction toBatchRead(item, idx) {\n const object = item.json.object || item.json.record || ORDER[idx];\n\n const results = Array.isArray(item.json.results) ? item.json.results : [];\n const ids = results.map(r => String(r.toObjectId)).filter(Boolean);\n\n return {\n json: {\n object,\n url: `https://api.hubapi.com/crm/v3/objects/${object}/batch/read`,\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: {\n properties: PROPS[object] || [],\n archived: false,\n inputs: ids.map(id => ({ id })),\n },\n hasInputs: ids.length > 0,\n count: ids.length,\n },\n };\n}\n\nreturn $input.all().map(toBatchRead);\n"
},
"typeVersion": 2
},
{
"id": "ee30292a-fce0-4e18-a422-3d7a58b82e4e",
"name": "批量读取对象",
"type": "n8n-nodes-base.httpRequest",
"position": [
1888,
256
],
"parameters": {
"url": "={{ $json.url }}",
"body": "={{ $json.body }}",
"method": "POST",
"options": {
"response": {}
},
"sendBody": true,
"contentType": "raw",
"authentication": "predefinedCredentialType",
"rawContentType": "={{ $json.headers['content-type'] }}",
"nodeCredentialType": "hubspotOAuth2Api"
},
"typeVersion": 4.2
},
{
"id": "e588dad3-5cdc-47f8-b180-d97d8e0bbb0a",
"name": "为 LLM 标准化 CRM 上下文",
"type": "n8n-nodes-base.code",
"position": [
2096,
256
],
"parameters": {
"jsCode": "// n8n Code node (JavaScript)\n// Input: three items (HubSpot batch/read responses) for deals, companies, tickets (order unknown)\n// Output: a single consolidated item with cleaned, LLM-ready fields\n\nconst items = $input.all().map(i => i.json);\n\n// --- helpers ---\nconst isNonEmpty = v => v !== null && v !== undefined && v !== '';\nconst stripNulls = obj =>\n Object.fromEntries(Object.entries(obj).filter(([, v]) => isNonEmpty(v)));\n\nfunction detectType(block) {\n const first = block?.results?.[0]?.properties || {};\n if ('dealname' in first || 'dealstage' in first) return 'deals';\n if ('hs_ticket_id' in first || 'hs_pipeline' in first) return 'tickets';\n if ('name' in first || 'industry' in first) return 'companies';\n return 'unknown';\n}\n\nfunction mapDeal(p) {\n return stripNulls({\n id: p.hs_object_id || p.id,\n name: p.dealname,\n stage: p.dealstage,\n amount: isNonEmpty(p.amount) ? Number(p.amount) : undefined,\n pipeline: p.pipeline,\n closeDate: p.closedate,\n ownerId: p.hubspot_owner_id,\n createdAt: p.createdate,\n lastUpdatedAt: p.hs_lastmodifieddate,\n });\n}\n\nfunction mapCompany(p) {\n // Derive a simple location string when possible\n const parts = [p.city, p.state, p.country].filter(isNonEmpty);\n const hq = parts.length ? parts.join(', ') : undefined;\n\n return stripNulls({\n id: p.hs_object_id || p.id,\n name: p.name,\n domain: p.domain,\n website: p.website,\n phone: p.phone,\n industry: p.industry,\n employees: isNonEmpty(p.numberofemployees) ? Number(p.numberofemployees) : undefined,\n annualRevenue: isNonEmpty(p.annualrevenue) ? Number(p.annualrevenue) : undefined,\n headquarters: hq,\n ownerId: p.hubspot_owner_id,\n createdAt: p.createdate,\n lastUpdatedAt: p.hs_lastmodifieddate,\n });\n}\n\nfunction mapTicket(p) {\n return stripNulls({\n id: p.hs_ticket_id || p.hs_object_id || p.id,\n subject: p.subject,\n description: p.content,\n pipelineId: p.hs_pipeline,\n stageId: p.hs_pipeline_stage,\n priority: p.hs_ticket_priority,\n createdAt: p.createdate,\n lastUpdatedAt: p.hs_lastmodifieddate,\n closedDate: p.closed_date,\n });\n}\n\n// --- collect ---\nconst out = { deals: [], companies: [], tickets: [] };\n\nfor (const block of items) {\n const t = detectType(block);\n const rows = Array.isArray(block.results) ? block.results : [];\n if (t === 'deals') {\n out.deals = rows.map(r => mapDeal(r.properties || {})).filter(o => Object.keys(o).length);\n } else if (t === 'companies') {\n out.companies = rows.map(r => mapCompany(r.properties || {})).filter(o => Object.keys(o).length);\n } else if (t === 'tickets') {\n out.tickets = rows.map(r => mapTicket(r.properties || {})).filter(o => Object.keys(o).length);\n }\n}\n\n// Optional high-level summary for the LLM\nout.summary = {\n dealCount: out.deals.length,\n companyCount: out.companies.length,\n ticketCount: out.tickets.length,\n};\n\n// Emit a single consolidated item\nreturn [{ json: out }];\n"
},
"typeVersion": 2
},
{
"id": "04448b5d-ac9c-417b-9246-3853e94303f0",
"name": "等待响应 - 批准自动回复",
"type": "n8n-nodes-base.slack",
"position": [
2768,
256
],
"webhookId": "ffb81691-54b1-43da-8b71-a4c45362901b",
"parameters": {
"select": "channel",
"message": "={{ $('Watch Gmail (New Inbound)').first().json.From }} sent you the following message:\n\n{{ $('Watch Gmail (New Inbound)').first().json.snippet }}\n\n\nHere is an auto-generated reply (press \"Approve\" to send it):\n\n{{ $json.output }}",
"options": {
"limitWaitTime": {
"values": {
"resumeUnit": "days"
}
}
},
"channelId": {
"__rl": true,
"mode": "list",
"value": "C09H7HTHRMG",
"cachedResultName": "all-n8n-slack-test"
},
"operation": "sendAndWait",
"authentication": "oAuth2"
},
"typeVersion": 2.3
},
{
"id": "464de728-acf5-4664-9469-f4f660d29ec6",
"name": "是否已批准?",
"type": "n8n-nodes-base.if",
"position": [
2976,
256
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "9313939d-ed39-4a91-b0c6-18512a9c4676",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.data.approved }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "0eb866f6-f229-48da-bf53-a19b0439c7a9",
"name": "便签",
"type": "n8n-nodes-base.stickyNote",
"position": [
976,
112
],
"parameters": {
"color": 7,
"width": 1280,
"height": 384,
"content": "## 获取 CRM 信息"
},
"typeVersion": 1
},
{
"id": "bb2fb3e1-dbcc-468f-9d0b-65e379aad792",
"name": "便签1",
"type": "n8n-nodes-base.stickyNote",
"position": [
496,
112
],
"parameters": {
"color": 7,
"width": 464,
"height": 384,
"content": "## 获取接收的邮件"
},
"typeVersion": 1
},
{
"id": "53796f4b-13c6-4af0-ad1b-199ac49ac096",
"name": "便签2",
"type": "n8n-nodes-base.stickyNote",
"position": [
2272,
112
],
"parameters": {
"color": 7,
"width": 400,
"height": 528,
"content": "## 撰写回复草稿"
},
"typeVersion": 1
},
{
"id": "39b501df-7fc7-470f-8aa4-6dacc05eb255",
"name": "便签3",
"type": "n8n-nodes-base.stickyNote",
"position": [
2688,
112
],
"parameters": {
"color": 7,
"width": 704,
"height": 384,
"content": "## 批准并回复"
},
"typeVersion": 1
},
{
"id": "86267d6e-aa6e-4521-a0fb-c823724b7e7d",
"name": "工作流概览",
"type": "n8n-nodes-base.stickyNote",
"position": [
0,
0
],
"parameters": {
"color": 5,
"width": 468,
"height": 624,
"content": "## 带 HubSpot 上下文的 AI 邮件回复 + Slack 批准"
},
"typeVersion": 1
},
{
"id": "f7cb3860-8006-401b-9c4a-2fbb3ca0aa68",
"name": "便签6",
"type": "n8n-nodes-base.stickyNote",
"position": [
3184,
432
],
"parameters": {
"color": 7,
"width": 376,
"height": 232,
"content": "### 💡 自定义此工作流"
},
"typeVersion": 1
}
],
"pinData": {
"Watch Gmail (New Inbound)": [
{
"To": "\"miha.ambroz@n8n.io\" <miha.ambroz@n8n.io>",
"id": "199823d41f5aa56f",
"From": "Miha Ambroz <miha.ambroz@pm.me>",
"labels": [
{
"id": "INBOX",
"name": "INBOX"
},
{
"id": "IMPORTANT",
"name": "IMPORTANT"
},
{
"id": "CATEGORY_PERSONAL",
"name": "CATEGORY_PERSONAL"
},
{
"id": "UNREAD",
"name": "UNREAD"
}
],
"Subject": "Hey",
"payload": {
"mimeType": "text/plain"
},
"snippet": "I forgot what I last ordered. Can you help me? Sent from Proton Mail Android",
"threadId": "199823d41f5aa56f",
"historyId": "585196",
"internalDate": "1758826671000",
"sizeEstimate": 4059
}
]
},
"connections": {
"If Approved?": {
"main": [
[
{
"node": "Reply to a message",
"type": "main",
"index": 0
}
]
]
},
"Set Record Types": {
"main": [
[
{
"node": "List Contact Associations",
"type": "main",
"index": 0
}
]
]
},
"Batch Read Objects": {
"main": [
[
{
"node": "Normalize CRM Context for LLM",
"type": "main",
"index": 0
}
]
]
},
"Find Contact by Email": {
"main": [
[
{
"node": "Set Record Types",
"type": "main",
"index": 0
}
]
]
},
"Draft Reply (AI Agent)": {
"main": [
[
{
"node": "Wait for Response - Approve Auto-Reply",
"type": "main",
"index": 0
}
]
]
},
"Filter: Allowed Sender": {
"main": [
[
{
"node": "Find Contact by Email",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "Draft Reply (AI Agent)",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Build Batch Read Requests": {
"main": [
[
{
"node": "Batch Read Objects",
"type": "main",
"index": 0
}
]
]
},
"List Contact Associations": {
"main": [
[
{
"node": "Build Batch Read Requests",
"type": "main",
"index": 0
}
]
]
},
"Watch Gmail (New Inbound)": {
"main": [
[
{
"node": "Filter: Allowed Sender",
"type": "main",
"index": 0
}
]
]
},
"Normalize CRM Context for LLM": {
"main": [
[
{
"node": "Draft Reply (AI Agent)",
"type": "main",
"index": 0
}
]
]
},
"Wait for Response - Approve Auto-Reply": {
"main": [
[
{
"node": "If Approved?",
"type": "main",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
高级 - 客户关系管理, AI 摘要总结
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
HubSpot 联系人 AI 增强
HubSpot 联系人 AI 增强
Filter
Hubspot
Agent
+5
12 节点Miha
客户关系管理
基于客户邮件的个性化外展
基于客户邮件的个性化外展
Set
Gmail
Hubspot
+6
17 节点Miha
客户关系管理
每周HubSpot潜在客户报告发送至Slack
每周HubSpot潜在客户报告发送至Slack
Slack
Filter
Hubspot
+3
11 节点Miha
客户关系管理
在可视化参考库中探索n8n节点
在可视化参考库中探索n8n节点
If
Ftp
Set
+93
113 节点I versus AI
其他
AI通话摘要 + HubSpot跟进任务
AI通话摘要 + HubSpot跟进任务
Hubspot
Form Trigger
Hubspot Tool
+4
12 节点Miha
客户关系管理
邮件转WhatsApp - AI多账户桥接
AI驱动的邮件转发至WhatsApp,集成Gmail、Outlook和Google Gemini
Code
Gmail
Http Request
+6
22 节点iTzJok3r
个人效率