ElevenLabs语音代理:医疗诊所预约预订
高级
这是一个自动化工作流,包含 21 个节点。主要使用 If, Set, Code, Gmail, Webhook 等节点。 使用ElevenLabs语音代理和Google日历自动化医疗预约
前置要求
- •Google 账号和 Gmail API 凭证
- •HTTP Webhook 端点(n8n 会自动生成)
分类
-
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"id": "KYehtlopDpAO49xz",
"meta": {
"instanceId": "84280226fffa0e8a3206dee30fe2043ca65af7920df5605b8479b2950b4310cc"
},
"name": "ElevenLabs 语音代理:医疗诊所预约预订",
"tags": [],
"nodes": [
{
"id": "3e4fd8c7-20a2-4e12-b628-e320a3d18fd8",
"name": "便签 - Webhook",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3936,
-272
],
"parameters": {
"color": 5,
"width": 510,
"height": 481,
"content": "## 📥 WEBHOOK 入口点"
},
"typeVersion": 1
},
{
"id": "09316f64-01a4-41a6-a5d7-fe188596d08c",
"name": "便签 - 编辑字段",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3424,
-272
],
"parameters": {
"color": 5,
"width": 310,
"height": 481,
"content": "## ✏️ 数据准备"
},
"typeVersion": 1
},
{
"id": "e50d4da0-11c4-482b-bf94-3cbde2d83b7e",
"name": "便签 - 路由",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3120,
-272
],
"parameters": {
"color": 6,
"width": 406,
"height": 485,
"content": "## 🔀 路由逻辑"
},
"typeVersion": 1
},
{
"id": "3d3dc615-146c-4602-af78-ed7484856b90",
"name": "便签 - 预订",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2704,
304
],
"parameters": {
"color": 3,
"width": 255,
"height": 274,
"content": "## ✅ 预订路径"
},
"typeVersion": 1
},
{
"id": "c16c3b25-795a-4ec0-89f5-b11545ea16b5",
"name": "便签 - 邮箱",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2448,
304
],
"parameters": {
"color": 3,
"width": 357,
"height": 274,
"content": "## 📧 邮件确认"
},
"typeVersion": 1
},
{
"id": "06216198-2728-4f2f-bf04-c981c25185a9",
"name": "便签 - 可用性",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2720,
-272
],
"parameters": {
"color": 7,
"width": 453,
"height": 482,
"content": "## 🔍 可用性检查路径"
},
"typeVersion": 1
},
{
"id": "56ffb539-4752-4a58-ac64-245a9ff11138",
"name": "便签 - 快速开始",
"type": "n8n-nodes-base.stickyNote",
"position": [
-4336,
-272
],
"parameters": {
"color": 4,
"width": 405,
"height": 900,
"content": "## 🚀 快速开始指南"
},
"typeVersion": 1
},
{
"id": "600186bf-6b72-4daf-9e0a-00a8cb247b36",
"name": "便签 - 故障排除",
"type": "n8n-nodes-base.stickyNote",
"position": [
-4336,
352
],
"parameters": {
"color": 4,
"width": 409,
"height": 524,
"content": "## 🐛 故障排除"
},
"typeVersion": 1
},
{
"id": "c5f730f6-8930-4efe-8773-5b93c6fd8e4e",
"name": "Webhook1",
"type": "n8n-nodes-base.webhook",
"position": [
-3696,
240
],
"webhookId": "2be0d61e-a2a0-48de-867e-4892849296b4",
"parameters": {
"path": "2be0d61e-a2a0-48de-867e-4892849296b4",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "aba4a40b-94f7-4f54-b1e1-87459cf96dca",
"name": "可用?1",
"type": "n8n-nodes-base.if",
"position": [
-2432,
48
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "1d0c317e-477a-437e-918a-a761e9069115",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.available }}",
"rightValue": "true"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "3cbee3f2-dd7a-4a09-84d7-0957aaac68b7",
"name": "再次检查可用性",
"type": "n8n-nodes-base.googleCalendar",
"position": [
-2176,
128
],
"parameters": {
"options": {},
"timeMax": "={{ DateTime.fromISO($('Webhook1').item.json.body.date).plus({ days: 30 }).toISODate() }}",
"timeMin": "={{ $('Webhook1').item.json.body.date }} ",
"calendar": {
"__rl": true,
"mode": "id",
"value": "="
},
"operation": "getAll",
"returnAll": true
},
"credentials": {
"googleCalendarOAuth2Api": {
"id": "twRjD6vIE5sHTNMP",
"name": "Google Calendar account"
}
},
"typeVersion": 1.3
},
{
"id": "7cafaa98-68fb-4408-b37b-ba1e7edb436f",
"name": "设置变量",
"type": "n8n-nodes-base.set",
"position": [
-3328,
240
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "4478c006-d158-4854-bf88-5e0fa1c8b936",
"name": "fullName",
"type": "string",
"value": "={{ $json.body.fullName }}"
},
{
"id": "b7bb33fc-426d-40ce-91b9-93844a269bfd",
"name": "email",
"type": "string",
"value": "={{ $json.body.email }}"
},
{
"id": "66f90820-45ae-4a98-ad17-f5ee72042680",
"name": "phone",
"type": "string",
"value": "={{ $json.body.phone }}"
},
{
"id": "0992fb3c-06e9-49f2-aba1-49fcee27172a",
"name": "location",
"type": "string",
"value": "={{ $json.body.location }}"
},
{
"id": "a969fb63-fee2-4e07-96d4-1f1468d8ed43",
"name": "appointmentType",
"type": "string",
"value": "={{ $json.body.appointmentType }}"
},
{
"id": "4444d238-b4ae-4bba-823a-aec64b4ea9de",
"name": "date",
"type": "string",
"value": "={{ $json.body.date }} {{ $json.body.time }}"
},
{
"id": "a4ef9aa5-11ca-4a35-b1f6-f0a20c64026f",
"name": "appointmentType",
"type": "string",
"value": "={{ $json.body.appointmentType }}"
},
{
"id": "6fff6dc5-0389-41c9-abfe-eaa4ddf7a2d8",
"name": "location",
"type": "string",
"value": "={{ $json.body.location }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "7e5cd44f-3b1e-4f6e-ab9f-59d9efd3ce84",
"name": "预订预约",
"type": "n8n-nodes-base.googleCalendar",
"position": [
-2640,
416
],
"parameters": {
"end": "={{ DateTime.fromFormat($('Set Up Variables').item.json.date, 'yyyy-MM-dd HH:mm').plus({ minutes: 30 }).toISO({ suppressMilliseconds: true }) }}",
"start": "={{ DateTime.fromFormat($('Set Up Variables').item.json.date, 'yyyy-MM-dd HH:mm').toISO({ suppressMilliseconds: true }) }}",
"calendar": {
"__rl": true,
"mode": "id",
"value": "="
},
"additionalFields": {
"summary": "={{ $json.fullName }}, {{ $('Webhook1').item.json.body.appointmentType }}",
"location": "={{ $('Webhook1').item.json.body.location }} ",
"attendees": [
"={{ $('Webhook1').item.json.body.email }}"
],
"description": "={{ $json.fullName }}\n\n{{ $('Webhook1').item.json.query.request }}"
},
"useDefaultReminders": false
},
"credentials": {
"googleCalendarOAuth2Api": {
"id": "twRjD6vIE5sHTNMP",
"name": "Google Calendar account"
}
},
"typeVersion": 1.3
},
{
"id": "0bbc9ee0-6813-4688-9638-dc4e79162b1a",
"name": "获取日历中的可用性",
"type": "n8n-nodes-base.googleCalendar",
"position": [
-2640,
48
],
"parameters": {
"options": {},
"timeMax": "={{ DateTime.fromFormat($('Set Up Variables').item.json.date, 'yyyy-MM-dd HH:mm').plus({ minutes: 30 }).toISO({ suppressMilliseconds: true }) }}",
"timeMin": "={{ DateTime.fromFormat($('Set Up Variables').item.json.date, 'yyyy-MM-dd HH:mm').toISO({ suppressMilliseconds: true }) }}",
"calendar": {
"__rl": true,
"mode": "id",
"value": "="
},
"resource": "calendar"
},
"credentials": {
"googleCalendarOAuth2Api": {
"id": "twRjD6vIE5sHTNMP",
"name": "Google Calendar account"
}
},
"typeVersion": 1.3
},
{
"id": "5c6da70a-f54f-4088-8ed1-4bf32c91cd86",
"name": "发送消息",
"type": "n8n-nodes-base.gmail",
"position": [
-2432,
416
],
"webhookId": "3d1d3b22-90ee-4cc0-a45b-acc4e6452e68",
"parameters": {
"message": "=Hello {{ $('Set Up Variables').item.json.fullName.split(' ')[0] }}! Your appointment for {{ $('Set Up Variables').item.json.appointmentType }} is confirmed – Evergreen Clinic on {{\n (() => {\n const dateStr = $('Set Up Variables').item.json.date; // e.g., \"2025-10-07 15:00\"\n const [datePart, timePart] = dateStr.split(' ');\n const [year, month, day] = datePart.split('-').map(Number);\n\n // Month names\n const months = [\n 'January', 'February', 'March', 'April', 'May', 'June',\n 'July', 'August', 'September', 'October', 'November', 'December'\n ];\n\n // Ordinal function\n function ordinal(n) {\n if (n > 3 && n < 21) return 'th';\n switch (n % 10) {\n case 1: return 'st';\n case 2: return 'nd';\n case 3: return 'rd';\n default: return 'th';\n }\n }\n\n return `${months[month - 1]} the ${day}${ordinal(day)} at ${timePart}`;\n })()\n}}<br><br> We're looking forward to seeing you. Here are the key details:<br><br> <b>Provider:</b> Dr. Sava (Aesthetic Medicine)<br> <b>Location:</b> Evergreen Clinic, Hasenbühlstrasse 36<br> <b>Duration:</b> ~20–30 minutes<br> <b>Contact:</b> 027 545 15 82 · evergreen@clinic.com<br><br> <b>Before you come (quick prep):</b> <ul> <li>Arrive 5–10 minutes early for check-in and a brief consent review.</li> <li>If possible, avoid alcohol, ibuprofen/aspirin, and intense workouts for 24 hours before (helps reduce bruising).</li> <li>Come with a clean face (no heavy makeup on treatment areas).</li> <li>Tell us in advance if you're pregnant/breastfeeding, have a skin infection, or take blood thinners—your safety first.</li> </ul> See you on {{ DateTime.fromISO($('Set Up Variables').item.json.date) .toFormat(\"MMMM d 'at' h:mm a\") .replace(/(\\d{1,2})(?= at)/, (d) => d + (['th','st','nd','rd'][((d%100-20)%10)||d%10]||'th')) }}!<br><br> Warm regards,<br> Evergreen Clinic<br> Rosenweg 24 3931, Zürich · 027 545 15 82",
"options": {
"appendAttribution": false
},
"subject": "={{ $('Set Up Variables').item.json.appointmentType }} at Evergreen Clinic on {{\n (() => {\n const dateStr = $('Set Up Variables').item.json.date; // e.g., \"2025-10-07 15:00\"\n const [datePart, timePart] = dateStr.split(' ');\n const [year, month, day] = datePart.split('-').map(Number);\n\n // Month names\n const months = [\n 'January', 'February', 'March', 'April', 'May', 'June',\n 'July', 'August', 'September', 'October', 'November', 'December'\n ];\n\n // Ordinal function\n function ordinal(n) {\n if (n > 3 && n < 21) return 'th';\n switch (n % 10) {\n case 1: return 'st';\n case 2: return 'nd';\n case 3: return 'rd';\n default: return 'th';\n }\n }\n\n return `${months[month - 1]} the ${day}${ordinal(day)} at ${timePart}`;\n })()\n}}"
},
"credentials": {
"gmailOAuth2": {
"id": "QZO6KobN7g8T6WvL",
"name": "Gmail account"
}
},
"typeVersion": 2.1
},
{
"id": "cda91dc7-8c9a-4ef5-ae69-38e894dd11ad",
"name": "确认预订",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-2224,
416
],
"parameters": {
"options": {},
"respondWith": "text",
"responseBody": "=Thank you. The appointment is scheduled on {{\n (() => {\n const dateStr = $('Set Up Variables').item.json.date; // e.g., \"2025-10-07 15:00\"\n const [datePart, timePart] = dateStr.split(' ');\n const [year, month, day] = datePart.split('-').map(Number);\n\n // Month names\n const months = [\n 'January', 'February', 'March', 'April', 'May', 'June',\n 'July', 'August', 'September', 'October', 'November', 'December'\n ];\n\n // Ordinal function\n function ordinal(n) {\n if (n > 3 && n < 21) return 'th';\n switch (n % 10) {\n case 1: return 'st';\n case 2: return 'nd';\n case 3: return 'rd';\n default: return 'th';\n }\n }\n\n return `${months[month - 1]} the ${day}${ordinal(day)} at ${timePart}`;\n })()\n}}"
},
"typeVersion": 1.4
},
{
"id": "ce9d6170-3b3f-486b-8667-0f0ec8e879dc",
"name": "确认可用时间",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-2176,
-32
],
"parameters": {
"options": {},
"respondWith": "text",
"responseBody": "=Thank you for waiting! This time is available"
},
"typeVersion": 1.4
},
{
"id": "ade07557-b674-4bb3-9415-cf0c98d6d095",
"name": "排序可用时间段",
"type": "n8n-nodes-base.code",
"position": [
-1968,
128
],
"parameters": {
"jsCode": "// Collect all booked intervals from input items (previous node)\nconst bookedIntervals = [];\nfor (const item of $input.all()) {\n bookedIntervals.push({\n start: new Date(item.json.start.dateTime),\n end: new Date(item.json.end.dateTime)\n });\n}\n\n// Settings\nconst days = 30;\nconst workStartHour = 7;\nconst workEndHour = 17; // exclusive, so last slot starts at 16:30\nconst slotDurationMinutes = 30;\n\n// Helper to format date to ISO8601 with +02:00\nfunction toISOWithFixedOffset(date) {\n const pad = n => String(n).padStart(2, '0');\n return date.getFullYear() + '-' +\n pad(date.getMonth() + 1) + '-' +\n pad(date.getDate()) + 'T' +\n pad(date.getHours()) + ':' +\n pad(date.getMinutes()) + ':00+02:00';\n}\n\n// Helper to check if two intervals overlap\nfunction isOverlapping(slotStart, slotEnd, bookedStart, bookedEnd) {\n return slotStart < bookedEnd && slotEnd > bookedStart;\n}\n\n// Get current time in +02:00 (Europe/Zurich) timezone\nconst now = new Date();\nconst nowUtc = now.getTime() + (now.getTimezoneOffset() * 60000);\nconst nowPlus2 = new Date(nowUtc + 2 * 60 * 60000);\n\nnowPlus2.setSeconds(0, 0); // ignore seconds/milliseconds\n\nlet available = [];\n\nfor (let d = 0; d < days; d++) {\n for (let h = workStartHour; h < workEndHour; h++) {\n for (let m = 0; m < 60; m += slotDurationMinutes) {\n let slotStart = new Date(nowPlus2);\n slotStart.setDate(slotStart.getDate() + d);\n slotStart.setHours(h, m, 0, 0);\n\n let slotEnd = new Date(slotStart);\n slotEnd.setMinutes(slotEnd.getMinutes() + slotDurationMinutes);\n\n // Exclude slots in the past\n if (slotStart < nowPlus2) continue;\n\n // Check for overlap with any booked interval\n let overlaps = bookedIntervals.some(b =>\n isOverlapping(slotStart, slotEnd, b.start, b.end)\n );\n\n if (!overlaps) {\n available.push({ available: toISOWithFixedOffset(slotStart) });\n }\n }\n }\n}\n\nreturn available.map(a => ({ json: a }));"
},
"typeVersion": 2
},
{
"id": "7527abf9-5e6d-44d2-8250-02db3eee78fa",
"name": "Aggregate",
"type": "n8n-nodes-base.aggregate",
"position": [
-1760,
128
],
"parameters": {
"options": {},
"fieldsToAggregate": {
"fieldToAggregate": [
{
"fieldToAggregate": "available"
}
]
}
},
"typeVersion": 1
},
{
"id": "729f6a0f-ad3e-4f4d-b5f4-d9695ecd1e03",
"name": "确认时间不可用",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-1552,
128
],
"parameters": {
"options": {},
"respondWith": "text",
"responseBody": "=Sorry, this time slot is unavailable right now. You may have another time in your mind?"
},
"typeVersion": 1.4
},
{
"id": "990395b5-8563-445a-bf87-9159427498ee",
"name": "如果",
"type": "n8n-nodes-base.if",
"position": [
-2976,
240
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "or",
"conditions": [
{
"id": "f586362a-6775-4692-b290-bc43bfdeb361",
"operator": {
"type": "string",
"operation": "notExists",
"singleValue": true
},
"leftValue": "={{ $json.fullName }}",
"rightValue": "check availability"
},
{
"id": "1b48aa09-72f2-4d20-8cac-e5e0adb80f28",
"operator": {
"type": "string",
"operation": "notExists",
"singleValue": true
},
"leftValue": "={{ $json.email }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
}
],
"active": false,
"pinData": {
"Webhook1": [
{
"json": {
"body": {
"date": "2025-10-08",
"time": "15:00",
"location": "Zurich",
"appointmentType": "botox + consult (new patients)"
},
"query": {
"request": "check availability"
},
"params": {},
"headers": {
"host": "jotona.app.n8n.cloud",
"accept": "*/*",
"cf-ray": "98ae5ad036c06095-ORD",
"cdn-loop": "cloudflare; loops=1; subreqs=1",
"cf-ew-via": "15",
"cf-worker": "n8n.cloud",
"x-real-ip": "34.59.11.47",
"cf-visitor": "{\"scheme\":\"https\"}",
"user-agent": "ElevenLabs/1.0",
"cf-ipcountry": "US",
"content-type": "application/json",
"x-is-trusted": "yes",
"content-length": "114",
"accept-encoding": "gzip, br",
"x-forwarded-for": "34.59.11.47, 172.69.6.226",
"cf-connecting-ip": "34.59.11.47",
"x-forwarded-host": "jotona.app.n8n.cloud",
"x-forwarded-port": "443",
"x-forwarded-proto": "https",
"x-forwarded-server": "traefik-prod-users-gwc-38-6f9f659dd9-vzwn5"
},
"webhookUrl": "https://jotona.app.n8n.cloud/webhook/2be0d61e-a2a0-48de-867e-4892849296b4",
"executionMode": "production"
}
}
]
},
"settings": {
"executionOrder": "v1"
},
"versionId": "eb4efa41-4863-44b4-a691-9f1553d2c4a9",
"connections": {
"990395b5-8563-445a-bf87-9159427498ee": {
"main": [
[
{
"node": "0bbc9ee0-6813-4688-9638-dc4e79162b1a",
"type": "main",
"index": 0
}
],
[
{
"node": "7e5cd44f-3b1e-4f6e-ab9f-59d9efd3ce84",
"type": "main",
"index": 0
}
]
]
},
"c5f730f6-8930-4efe-8773-5b93c6fd8e4e": {
"main": [
[
{
"node": "7cafaa98-68fb-4408-b37b-ba1e7edb436f",
"type": "main",
"index": 0
}
]
]
},
"7527abf9-5e6d-44d2-8250-02db3eee78fa": {
"main": [
[
{
"node": "729f6a0f-ad3e-4f4d-b5f4-d9695ecd1e03",
"type": "main",
"index": 0
}
]
]
},
"aba4a40b-94f7-4f54-b1e1-87459cf96dca": {
"main": [
[
{
"node": "ce9d6170-3b3f-486b-8667-0f0ec8e879dc",
"type": "main",
"index": 0
}
],
[
{
"node": "3cbee3f2-dd7a-4a09-84d7-0957aaac68b7",
"type": "main",
"index": 0
}
]
]
},
"5c6da70a-f54f-4088-8ed1-4bf32c91cd86": {
"main": [
[
{
"node": "cda91dc7-8c9a-4ef5-ae69-38e894dd11ad",
"type": "main",
"index": 0
}
]
]
},
"7e5cd44f-3b1e-4f6e-ab9f-59d9efd3ce84": {
"main": [
[
{
"node": "5c6da70a-f54f-4088-8ed1-4bf32c91cd86",
"type": "main",
"index": 0
}
]
]
},
"7cafaa98-68fb-4408-b37b-ba1e7edb436f": {
"main": [
[
{
"node": "990395b5-8563-445a-bf87-9159427498ee",
"type": "main",
"index": 0
}
]
]
},
"ade07557-b674-4bb3-9415-cf0c98d6d095": {
"main": [
[
{
"node": "7527abf9-5e6d-44d2-8250-02db3eee78fa",
"type": "main",
"index": 0
}
]
]
},
"3cbee3f2-dd7a-4a09-84d7-0957aaac68b7": {
"main": [
[
{
"node": "ade07557-b674-4bb3-9415-cf0c98d6d095",
"type": "main",
"index": 0
}
]
]
},
"0bbc9ee0-6813-4688-9638-dc4e79162b1a": {
"main": [
[
{
"node": "aba4a40b-94f7-4f54-b1e1-87459cf96dca",
"type": "main",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
高级
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
在可视化参考库中探索n8n节点
在可视化参考库中探索n8n节点
If
Ftp
Set
+
If
Ftp
Set
113 节点I versus AI
其他
AI工时表生成器 - 集成Gmail、日历和GitHub到Google表格
AI工时表生成器 - 集成Gmail、日历和GitHub到Google表格
If
Set
Code
+
If
Set
Code
31 节点Luka Zivkovic
个人效率
AI驱动的自动化求职与申请工作流
AI驱动的自动化求职与申请
If
Set
Code
+
If
Set
Code
21 节点Gerald Denor
人力资源
基于Bright Data、OpenAI和Redis的高级多源AI研究
使用Bright Data、OpenAI和Redis进行高级多源AI研究
If
Set
Code
+
If
Set
Code
43 节点Daniel Shashko
市场调研
竞争对手内容差距分析器:自动化网站主题映射
使用Gemini AI、Apify和Google Sheets分析竞争对手内容差距
If
Set
Code
+
If
Set
Code
30 节点Mychel Garzon
杂项
分析Gmail邮件头以检测IP信誉和欺骗
分析Gmail邮件头以检测IP信誉和欺骗
If
Set
Code
+
If
Set
Code
40 节点Angel Menendez
安全运维