This workflow corresponds to n8n.io template #8043 — we link there as the canonical source.
This workflow follows the Agent → Gmail recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "SAqkCDqXBdN0UN0F",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Dental Clinic Receptionist",
"tags": [],
"nodes": [
{
"id": "6cb4cb42-b5e8-446d-9711-d72649c8b6cc",
"name": "Check Availability",
"type": "n8n-nodes-base.googleCalendarTool",
"position": [
624,
928
],
"parameters": {
"options": {},
"timeMax": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('End_Time', ``, 'string') }}",
"timeMin": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Start_Time', ``, 'string') }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "user@example.com",
"cachedResultName": "n8n"
},
"resource": "calendar"
},
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "8b070be5-d1b2-4f02-99a4-6278450b9fe7",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"position": [
304,
656
],
"parameters": {
"path": "getInput",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "69b5f76a-3c63-4e0c-b4cb-d52e7dc57cbf",
"name": "Respond to Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
912,
656
],
"parameters": {
"options": {}
},
"typeVersion": 1.4
},
{
"id": "b55f6c71-39df-46f8-8439-2f6e3e125466",
"name": "Window Buffer Memory",
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"position": [
400,
912
],
"parameters": {
"sessionKey": "={{ $json.body.chatId }}",
"sessionIdType": "customKey",
"contextWindowLength": 100
},
"typeVersion": 1.3
},
{
"id": "d48200d0-fbf4-4cef-bcd9-f0d9a6b66579",
"name": "OpenRouter Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
"position": [
240,
896
],
"parameters": {
"model": "openai/gpt-4o-mini",
"options": {}
},
"credentials": {
"openRouterApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "39947ea3-452c-4c93-bdaa-9639a16a574a",
"name": "AI Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
560,
656
],
"parameters": {
"text": "={{ $json.body.query }}",
"options": {
"systemMessage": "=You are a virtual assistant specializing in appointment management for Dr. Hakim. \nYour role is to manage consultations accurately, ensuring real availability while providing a smooth experience for patients.\n\nOffice Hours:\n- Monday \u2013 Friday: 9:00 AM \u2013 8:00 PM\n- Saturday: 9:00 AM \u2013 1:00 PM\n- Sunday: Closed\n- Consultation Duration: 1 hour\n- Break Between Patients: 15 minutes\n\nBooking Process:\n1. Collect Patient Information:\n - Full Name\n - Phone Number\n - Email Address\n - Desired Date and Time\n If any information is missing, politely ask the patient before proceeding.\n\n2. Availability Check:\n - Always use the \"Check Availability\" tool to verify free slots in Google Calendar.\n - If requested time overlaps with an existing appointment, suggest the next available slot.\n - Do not create or confirm an appointment until the user explicitly approves the slot.\n\n3. Booking Confirmation:\n - Only create the appointment using the \"Create Event\" tool after explicit user confirmation.\n - Do not say \"appointment booked\" unless the tool is actually used.\n - Appointment ID format: APT-YYYYMMDD-XXXXXX (6 random alphanumeric characters).\n - Event Title: Patient Name - Phone Number\n - Event Description: Include full name, phone, email, appointment ID, date & time.\n\nConfirmation Message Format:\nWhen booking is complete, return only this message:\n\u2705 Appointment Confirmed:\n- Full Name: {full_name}\n- Phone: {phone}\n- Email: {email}\n- Date: {date}\n- Time: {time}\n- Appointment ID: {appointment_id}\n\nDo NOT include:\n- Calendar event links\n- Extra text after the appointment ID\n- Any confirmation without Create Event tool usage\n\nCancellations:\nIf asked to cancel, reply: \"Please check your booking email. I cannot cancel appointments for you.\"\n\nVerification:\nIf asked to check an appointment by ID, reply: \"Check your email we sent when you booked.\"\n\nTools Available:\n- Check Availability \u2192 Verify free slots in Google Calendar\n- Create Event \u2192 Book confirmed appointment in Google Calendar\n\nCommunication Rules:\n- Always be polite, clear, and friendly.\n- Never assume missing details\u2014ask for them.\n- Never create or cancel without explicit confirmation.\n- Stick strictly to availability check and booking after confirmation.\n\nToday's Date: {{ $now }}"
},
"promptType": "=define"
},
"typeVersion": 1.7
},
{
"id": "cd82287b-0ea8-4e15-a8da-9e5c4bb7bcb2",
"name": "Code",
"type": "n8n-nodes-base.code",
"position": [
1248,
1024
],
"parameters": {
"jsCode": "// Get the AI Agent output (which is a JSON string)\nconst msg = $input.first().json.output || \"\";\n\nif (!msg) {\n throw new Error(\"AI Agent output not found.\");\n}\n\n// Parse the JSON string\nlet data;\ntry {\n data = JSON.parse(msg);\n} catch (err) {\n throw new Error(\"Failed to parse AI Agent output as JSON: \" + err.message);\n}\n\n// Fallback Appointment ID if missing\nif (!data.appointment_id || data.appointment_id === \"null\") {\n const randomId = Math.random().toString(36).substring(2, 8).toUpperCase();\n data.appointment_id = `APT-${randomId}`;\n}\n\nreturn [{ json: data }];\n"
},
"typeVersion": 2
},
{
"id": "2e634902-e4ce-4362-8ca2-5fa1bb4ebd73",
"name": "If",
"type": "n8n-nodes-base.if",
"position": [
1456,
1024
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "f1cbfa92-a423-4baf-8ab1-f8cc8ab77038",
"operator": {
"type": "string",
"operation": "notEquals"
},
"leftValue": "={{ $json.patient.full_name }}",
"rightValue": "null"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "cd01ecc0-431a-4c8b-a703-dbcef7075fcd",
"name": "Append row in sheet",
"type": "n8n-nodes-base.googleSheets",
"position": [
1152,
1232
],
"parameters": {
"columns": {
"value": {
"Time": "={{ $json.appointment.time }}",
"Date ": "={{ $json.appointment.date }}",
"Email": "={{ $json.patient.email }}",
"Name ": "={{ $json.patient.full_name }}",
"Phone Number": "={{ $json.patient.phone }}",
"Appointment ID": "={{ $json.appointment_id }}"
},
"schema": [
{
"id": "Appointment ID",
"type": "string",
"display": true,
"required": false,
"displayName": "Appointment ID",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Name ",
"type": "string",
"display": true,
"required": false,
"displayName": "Name ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email",
"type": "string",
"display": true,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Phone Number",
"type": "string",
"display": true,
"required": false,
"displayName": "Phone Number",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Date ",
"type": "string",
"display": true,
"required": false,
"displayName": "Date ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Time",
"type": "string",
"display": true,
"required": false,
"displayName": "Time",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.YOUR_AWS_SECRET_KEY_HERE-BSY47v486qkfGCA3gE8PKw/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "13HOoBmWcxkL8cUZtWFU1-BSY47v486qkfGCA3gE8PKw",
"cachedResultUrl": "https://docs.google.YOUR_AWS_SECRET_KEY_HERE-BSY47v486qkfGCA3gE8PKw/edit?usp=drivesdk",
"cachedResultName": "dr"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.7
},
{
"id": "ac1a8cc3-7d80-4885-8360-0dce48b2af89",
"name": "For Doctor",
"type": "n8n-nodes-base.gmail",
"position": [
1376,
1232
],
"parameters": {
"sendTo": "user@example.com",
"message": "=Hello Dr. Hakim,\nA new appointment has been scheduled. Here are the details: \nPatient Name: {{ $json['Name '] }}\nPhone Number: {{ $json['Phone Number'] }}\nEmail: {{ $json.Email }} \nAppointment Date: {{ $json['Date '] }} \nTime: {{ $json.Time }}\nAppointment ID:{{ $json['Appointment ID'] }} \nPlease check your Google Calendar for more details.",
"options": {
"appendAttribution": false
},
"subject": "New Appointment Scheduled",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "88e1c616-7014-4d49-8d32-fe9c77f85d86",
"name": "For User",
"type": "n8n-nodes-base.gmail",
"position": [
1632,
1232
],
"parameters": {
"sendTo": "={{ $('Append row in sheet').item.json.Email }}",
"message": "=Dear {{ $('Append row in sheet').item.json['Name '] }},\nYour appointment with Dr. Hakim has been successfully booked! Here are the details: \nName: {{ $('Append row in sheet').item.json['Name '] }}\nPhone Number: {{ $('Append row in sheet').item.json['Phone Number'] }}\nEmail: {{ $('Append row in sheet').item.json.Email }}\nAppointment Date: {{ $('Append row in sheet').item.json['Date '] }}\nTime: {{ $('Append row in sheet').item.json.Time }}\nAppointment ID:{{ $('Append row in sheet').item.json['Appointment ID'] }}\nPlease keep this Appointment ID for your reference. If you have any questions or need to cancel your appointment, feel free to contact us. \nBest regards, \nDr. Hakim's Clinic",
"options": {
"appendAttribution": false
},
"subject": "Appointment Confirmation \u2013 Dr. Hakim",
"emailType": "text"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "7eec2403-de34-4163-8984-3c5f6be8070c",
"name": "Creat event",
"type": "n8n-nodes-base.googleCalendarTool",
"position": [
832,
912
],
"parameters": {
"end": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('End', ``, 'string') }}",
"start": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Start', ``, 'string') }}",
"calendar": {
"__rl": true,
"mode": "list",
"value": "user@example.com",
"cachedResultName": "n8n"
},
"additionalFields": {}
},
"credentials": {
"googleCalendarOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "ab252537-767c-477d-9a0b-941bd4ed8558",
"name": "AI Agent1",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
1168,
656
],
"parameters": {
"text": "={{$input.first().json.output}}",
"options": {
"systemMessage": "You are an Appointment Parsing Agent.\nYour job is to extract appointment details from text and return them as clean JSON.\n\n\ud83d\udd39 Rules\n\nAlways return data strictly in this JSON format:\n\n{\n \"patient\": {\n \"full_name\": \"John Doe\",\n \"phone\": \"123456789\",\n \"email\": \"user@example.com\"\n },\n \"appointment\": {\n \"date\": \"29th August 2025\",\n \"time\": \"5 PM\"\n },\n \"appointment_id\": \"APT20250831KY5T4N\"\n}\n\n\nAppointment ID:\n\nIf provided, return it exactly as-is.\n\nDo not add extra text (e.g. \"this is a unique ID for your appointment\").\n\nIf missing, generate one in this format:\n\nAPT + YYYYMMDD + 5 random alphanumeric characters\n\n\nExample: APT20250831KY5T4N\n\nAll fields must always be present in JSON:\n\npatient.full_name\n\npatient.phone\n\npatient.email\n\nappointment.date\n\nappointment.time\n\nappointment_id\n\nIf a value is missing, set it to \"null\" (string).\n\nOutput only valid JSON. No explanations, no Markdown, no extra text."
},
"promptType": "define"
},
"typeVersion": 2.2
},
{
"id": "f07a734c-618e-47a7-81d0-2a8f771f7fe1",
"name": "OpenRouter Chat Model1",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
"position": [
1040,
848
],
"parameters": {
"model": "openai/gpt-4o-mini",
"options": {}
},
"credentials": {
"openRouterApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
}
],
"active": true,
"settings": {
"timezone": "Europe/Rome",
"callerPolicy": "workflowsFromSameOwner",
"executionOrder": "v1"
},
"versionId": "0fac2f0e-7ed9-4fbc-91c8-c318f819b03c",
"connections": {
"If": {
"main": [
[
{
"node": "Append row in sheet",
"type": "main",
"index": 0
}
]
]
},
"Code": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"Webhook": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
},
"AI Agent1": {
"main": [
[
{
"node": "Code",
"type": "main",
"index": 0
}
]
]
},
"For Doctor": {
"main": [
[
{
"node": "For User",
"type": "main",
"index": 0
}
]
]
},
"Creat event": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Check Availability": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Respond to Webhook": {
"main": [
[
{
"node": "AI Agent1",
"type": "main",
"index": 0
}
]
]
},
"Append row in sheet": {
"main": [
[
{
"node": "For Doctor",
"type": "main",
"index": 0
}
]
]
},
"Window Buffer Memory": {
"ai_memory": [
[
{
"node": "AI Agent",
"type": "ai_memory",
"index": 0
}
]
]
},
"OpenRouter Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"OpenRouter Chat Model1": {
"ai_languageModel": [
[
{
"node": "AI Agent1",
"type": "ai_languageModel",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
gmailOAuth2googleCalendarOAuth2ApigoogleSheetsOAuth2ApiopenRouterApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
🦷 Dental Clinic Appointment Booking System (n8n)
Source: https://n8n.io/workflows/8043/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Enhance your support, onboarding, and internal knowledge workflows with an intelligent RAG-powered chatbot that responds using live data stored in Google Sheets. 🤖📚 Built for teams that rely on struct
leads. Uses supabase, gmail, formTrigger, httpRequest. Webhook trigger; 62 nodes.
Hi! I'm Amanda — I create smart, useful AI automations for n8n and Make. This workflow helps you monitor how many tokens are consumed per conversation with AI Agent Tokens, and logs that usage into Go
Housemate. Uses telegramTrigger, openAi, googleDrive, telegram. Event-driven trigger; 34 nodes.
Nps-Com-Atendimento-Ao-Cliente. Uses toolSerpApi, lmChatOpenAi, microsoftSql, gmail. Webhook trigger; 34 nodes.