This workflow corresponds to n8n.io template #11463 — we link there as the canonical source.
This workflow follows the Agent → OpenAI Chat 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 →
{
"nodes": [
{
"id": "f8aee857-4624-4e72-9a98-78940c8523b3",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"position": [
-3168,
-1104
],
"parameters": {
"path": "3beec299-79db-453e-8986-b1b98e82209d",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "ec7ed967-6c5d-4722-b44f-065b8b5ecaa4",
"name": "Switch",
"type": "n8n-nodes-base.switch",
"position": [
-2960,
-1200
],
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "5aec7e20-2e74-4281-9dfd-407e307df5fc",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.body.args.tool_name }}",
"rightValue": "set_appointment"
}
]
}
},
{
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "19e72212-61f8-4988-b712-e81fd0a05c7d",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.body.args.tool_name }}",
"rightValue": "reschedule"
}
]
}
},
{
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "b2c400f0-2e7b-412a-b3c0-09a1723f2622",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.body.args.tool_name }}",
"rightValue": "cancel"
}
]
}
},
{
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "14d874f0-bf14-4024-adc3-ca40d346e3dc",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.body.args.tool_name }}",
"rightValue": "insurance_check"
}
]
}
},
{
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "222aad52-9fa3-4610-b876-c6640e7052a4",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.body.args.tool_name }}",
"rightValue": "get_appointmnets_list"
}
]
}
},
{
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "376893e4-cd06-48b8-9d37-92c5bbfcb35a",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.body.args.tool_name }}",
"rightValue": "get_availability"
}
]
}
},
{
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "18530e6f-b2f9-4284-ae79-6f8e06a1a6d1",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.body.args.tool_name }}",
"rightValue": "get_doctors_list"
}
]
}
}
]
},
"options": {}
},
"typeVersion": 3.2
},
{
"id": "262da778-2e71-4392-ad83-d3ac4629d8e7",
"name": "Notify Appointment Time",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-1136,
-2400
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\n \"type\": \"conversation_initiation_client_data\",\n \"conversation_config_override\": {\n \"agent\": {},\n \"tts\": {}\n },\n \"custom_llm_extra_body\": {\n \"temperature\": 0.7,\n \"max_tokens\": 150\n },\n \"dynamic_variables\": {\n \"date\": \"{{ $json.date }}\",\n \"name\": \"{{ $json.name }}\"\n }\n}\n"
},
"typeVersion": 1.2
},
{
"id": "927afe66-a2e5-4c20-8ad6-ed8e42c6c401",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-4304,
-2832
],
"parameters": {
"width": 752,
"height": 960,
"content": "## Dental Clinic Automation: Scheduling, Availability & Patient Lookup\nThis workflow automates dental appointment management through a phone-based assistant. It listens for requests like booking, rescheduling, canceling, checking insurance, looking up appointments, and finding available time slots. Each request is processed through a Switch node and then routed to your Supabase database for action.\n\n## How it works\nOnce a request is received, the workflow uses the patient\u2019s phone number to identify them. Then, it:\n- Booking: Checks for available time, creates or retrieves the patient record, and stores the appointment.\n- Rescheduling: Confirms the new date, avoids double-booking, and updates the record.\n- Canceling: Removes the appointment and sends a confirmation.\n- Insurance: Looks up the member ID and provides a status (accepted or not).\n- Availability: Finds the doctor\u2019s existing appointments and generates available 60-minute slots.\n- Appointment & doctor lists: Retrieves and presents clean, structured information for the assistant.\nEach action ends with a webhook response that the phone system reads back to the patient.\n\n## Setup steps\n1. Add your Supabase credentials to the Supabase nodes.\n2. Connect your phone/voice system to the webhook URL.\n3. Ensure Supabase table and column names match the workflow.\n4. Test all actions (booking, rescheduling, canceling, etc.) before going live.\n\n## Customization tips (optional)\nYou can update working hours, appointment durations, or add new services by modifying the availability logic or Switch node routing."
},
"typeVersion": 1
},
{
"id": "87c59956-3ce8-4d21-93b0-c47b306f137b",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3360,
-1904
],
"parameters": {
"color": 7,
"width": 800,
"height": 1040,
"content": "## Request Routing & Patient Lookup\nThis section receives the webhook request from the phone assistant, reads the user\u2019s intent (tool_name), and directs the flow into the correct branch. It also looks up the patient or insurance record in Supabase so each action\u2014booking, rescheduling, canceling, listing, insurance check\u2014starts with the right user data."
},
"typeVersion": 1
},
{
"id": "23b94806-0330-4b19-8122-71afb10493bb",
"name": "Find Patient (Booking)",
"type": "n8n-nodes-base.supabase",
"position": [
-2752,
-1792
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "phone_number",
"keyValue": "={{ $json.body.args.phone_number }}"
}
]
},
"tableId": "dental_patients_canada",
"operation": "get"
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "58b80622-69d7-4848-8e67-cf574570dca8",
"name": "Find Patient (Reschedule)",
"type": "n8n-nodes-base.supabase",
"position": [
-2752,
-1632
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "phone_number",
"keyValue": "={{ $json.body.args.phone_number }}"
}
]
},
"tableId": "dental_patients_canada",
"operation": "get"
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "5e156ab3-875d-4872-b2ab-7aefd762542c",
"name": "Find Patient (Cancel)",
"type": "n8n-nodes-base.supabase",
"position": [
-2752,
-1456
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "phone_number",
"keyValue": "={{ $json.body.args.Phone_Number }}"
}
]
},
"tableId": "dental_patients_canada",
"operation": "get"
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "41d4e0fa-8e50-45bc-a001-9f391b117acd",
"name": "Find Patient (List Appointments)",
"type": "n8n-nodes-base.supabase",
"position": [
-2752,
-1296
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "member_id",
"keyValue": "={{ $json.body.args.member_id }}"
}
]
},
"tableId": "dental_insurance",
"operation": "get"
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "b76cff25-2d22-4709-b098-67657d93a969",
"name": "Find Insurance Record",
"type": "n8n-nodes-base.supabase",
"position": [
-2752,
-1120
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "phone_number",
"keyValue": "={{ $json.body.args.Phone_Number }}"
}
]
},
"tableId": "dental_patients_canada",
"operation": "get"
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "9cec9373-abec-4b5d-a1b2-8b41a2a5d1f2",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2512,
-2640
],
"parameters": {
"color": 7,
"width": 784,
"height": 400,
"content": "## Booking: Check Availability & Create Appointment\nThis section checks whether the selected time is free, verifies if the patient already exists, and then creates a new appointment in Supabase. If the slot is unavailable, the flow returns an error message; if it\u2019s free, the booking is inserted and confirmed."
},
"typeVersion": 1
},
{
"id": "6f66de57-efec-4fc3-92f6-d95cb87eea4c",
"name": "Check Time Availability",
"type": "n8n-nodes-base.if",
"position": [
-2448,
-2464
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "6bd37859-ceb6-4415-a070-5d29e2964059",
"operator": {
"type": "number",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $('Find Patient (Booking)').item.json.id }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "1cf43723-0b40-4514-942c-210337279e20",
"name": "Get Existing Appointments",
"type": "n8n-nodes-base.supabase",
"position": [
-2064,
-2512
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "appointment_date",
"keyValue": "={{ $('Webhook').item.json.body.args.start }}",
"condition": "eq"
},
{
"keyName": "doctor_id",
"keyValue": "={{ $('Webhook').item.json.body.args.doctor_id }}",
"condition": "eq"
}
]
},
"tableId": "dental_appointments_canada",
"matchType": "allFilters",
"operation": "getAll",
"returnAll": true
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "c248ce2b-0ea4-4ffd-9e08-3112013a71dc",
"name": "Create Appointment",
"type": "n8n-nodes-base.supabase",
"position": [
-2224,
-2400
],
"parameters": {
"tableId": "dental_patients_canada",
"fieldsUi": {
"fieldValues": [
{
"fieldId": "name",
"fieldValue": "={{ $('Webhook').item.json.body.args.name }}"
},
{
"fieldId": "phone_number",
"fieldValue": "={{ $('Webhook').item.json.body.args.phone_number }}"
},
{
"fieldId": "email",
"fieldValue": "={{ $('Webhook').item.json.body.args.email }}"
},
{
"fieldId": "doctor_id",
"fieldValue": "={{ $('Webhook').item.json.body.args.doctor_id }}"
}
]
}
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "5c3f9187-ea34-46b6-b8c9-fe49f67db810",
"name": "Time Slot Already Taken?",
"type": "n8n-nodes-base.if",
"position": [
-1888,
-2512
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "059019bd-254b-41c0-9582-1e85e13ef6e3",
"operator": {
"type": "number",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $('Get Existing Appointments').item.json.id }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "7260bd03-80e1-4d64-b12e-9cd65dcdd764",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1664,
-2720
],
"parameters": {
"color": 7,
"width": 720,
"height": 496,
"content": "## Appointment Confirmation & Notifications\nThis section verifies the patient\u2019s phone number, sets the appointment details in the database, and sends notifications confirming the appointment time. If a patient already has a booking at the desired time, they are notified that the slot is taken. This ensures smooth communication with the patient."
},
"typeVersion": 1
},
{
"id": "b1aa1f38-2e2b-4cff-bb31-14629a1649b4",
"name": "Notify Appointment Conflict",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-1600,
-2560
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "{\n \"type\": \"conversation_initiation_client_data\",\n \"conversation_config_override\": {\n \"agent\": {},\n \"tts\": {}\n },\n \"custom_llm_extra_body\": {\n \"temperature\": 0.7,\n \"max_tokens\": 150\n },\n \"dynamic_variables\": {\n \"Message\": \"That time is already booked.\" }\n}\n"
},
"typeVersion": 1.2
},
{
"id": "627f018e-c5e2-45fd-9e4c-762a1418b4e9",
"name": "Verify Patient Phone Number",
"type": "n8n-nodes-base.supabase",
"position": [
-1600,
-2400
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "phone_number",
"keyValue": "={{ $('Webhook').item.json.body.args.phone_number }}"
}
]
},
"tableId": "dental_patients_canada",
"operation": "get"
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "8d9ff03f-0ac7-42e0-8c8a-78a3e2aaf23f",
"name": "Create Appointment Record",
"type": "n8n-nodes-base.supabase",
"position": [
-1360,
-2400
],
"parameters": {
"tableId": "dental_appointments_canada",
"fieldsUi": {
"fieldValues": [
{
"fieldId": "patient_id",
"fieldValue": "={{ $json.id }}"
},
{
"fieldId": "doctor_id",
"fieldValue": "={{ $('Webhook').item.json.body.args.doctor_id }}"
},
{
"fieldId": "appointment_date",
"fieldValue": "={{ $('Webhook').item.json.body.args.start }}"
},
{
"fieldId": "reason",
"fieldValue": "={{ $('Webhook').item.json.body.args.service_type }}"
}
]
}
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "ae0b5449-76be-4689-9c34-f9821b8af3ca",
"name": "Fetch Patient's Current Appointment",
"type": "n8n-nodes-base.supabase",
"position": [
-2480,
-2064
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "patient_id",
"keyValue": "={{ $json.id }}",
"condition": "eq"
},
{
"keyName": "appointment_date",
"keyValue": "={{ $('Webhook').item.json.body.args.start }}",
"condition": "eq"
}
]
},
"tableId": "dental_appointments_canada",
"matchType": "allFilters",
"operation": "getAll",
"returnAll": true
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "1426f03b-9985-4dc5-9461-096b2cd0e347",
"name": "Check Doctor Availability for New Slot",
"type": "n8n-nodes-base.supabase",
"position": [
-2272,
-2064
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "appointment_date",
"keyValue": "={{ $('Webhook').item.json.body.args.preferred_date }}",
"condition": "eq"
},
{
"keyName": "doctor_id",
"keyValue": "={{ $('Webhook').item.json.body.args.doctor_id }}",
"condition": "eq"
}
]
},
"tableId": "dental_appointments_canada",
"matchType": "allFilters",
"operation": "getAll",
"returnAll": true
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "24fba949-9925-4fd8-97a2-62bd195d0e90",
"name": "Delete Appointment Record",
"type": "n8n-nodes-base.supabase",
"position": [
-2448,
-1648
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "patient_id",
"keyValue": "={{ $json.id }}",
"condition": "eq"
},
{
"keyName": "appointment_date",
"keyValue": "={{ $('Webhook').item.json.body.args.start }}",
"condition": "eq"
}
]
},
"tableId": "dental_appointments_canada",
"matchType": "allFilters",
"operation": "delete"
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "2d8a4aca-6adf-4b1b-b2a8-00ab515b812b",
"name": "Notify Cancelation Success",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-2224,
-1648
],
"parameters": {
"options": {},
"respondWith": "text",
"responseBody": "{\n \"type\": \"conversation_initiation_client_data\",\n \"conversation_config_override\": {\n \"agent\": {},\n \"tts\": {}\n },\n \"custom_llm_extra_body\": {\n \"temperature\": 0.7,\n \"max_tokens\": 150\n },\n \"dynamic_variables\": {\n \"Message\": \"The appointment has been canceled successfully.\" }\n}\n\n"
},
"typeVersion": 1.2
},
{
"id": "c24dcbec-f7c7-4423-aed2-76e51c628595",
"name": "Check Insurance Status",
"type": "n8n-nodes-base.if",
"position": [
-2464,
-1184
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "a72fc1a7-6ee8-43b9-a585-1d02d8edeab6",
"operator": {
"type": "number",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.id }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "ccf237ce-a216-4b36-85a7-3d2874ea051b",
"name": "Notify Insurance Accepted",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-2240,
-1264
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "{\n \"type\": \"conversation_initiation_client_data\",\n \"conversation_config_override\": {\n \"agent\": {},\n \"tts\": {}\n },\n \"custom_llm_extra_body\": {\n \"temperature\": 0.7,\n \"max_tokens\": 150\n },\n \"dynamic_variables\": {\n \"Message\": \"Insurance accepted.\" }\n}\n\n\n\n\n"
},
"typeVersion": 1.2
},
{
"id": "faec5c92-e091-41c9-88a6-72eb8a849667",
"name": "Notify Insurance Rejected",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-2112,
-1152
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "{\n \"type\": \"conversation_initiation_client_data\",\n \"conversation_config_override\": {\n \"agent\": {},\n \"tts\": {}\n },\n \"custom_llm_extra_body\": {\n \"temperature\": 0.7,\n \"max_tokens\": 150\n },\n \"dynamic_variables\": {\n \"Message\": \"Insurance not accepted.\" }\n}\n\n\n\n\n"
},
"typeVersion": 1.2
},
{
"id": "10ea2de6-d5b8-48f1-8678-696a628374da",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2528,
-928
],
"parameters": {
"color": 7,
"width": 1088,
"height": 304,
"content": "## Retrieve & Summarize Appointments\nThis section gathers a list of the patient\u2019s appointments, aggregates the data, and summarizes it for easy retrieval. It fetches the appointments based on the patient\u2019s ID, then returns a summarized list of their upcoming appointments. This list is formatted and sent back to the user in a clean response."
},
"typeVersion": 1
},
{
"id": "b47c9b08-5317-4966-9500-0200d8ea1577",
"name": "Summarize Appointment Data",
"type": "n8n-nodes-base.summarize",
"position": [
-2048,
-768
],
"parameters": {
"options": {},
"fieldsToSplitBy": "data",
"fieldsToSummarize": {
"values": [
{
"field": "data",
"aggregation": "append"
}
]
}
},
"typeVersion": 1.1
},
{
"id": "8f0b09dc-fed8-45af-989e-05ef85192705",
"name": "Fetch Patient Appointments",
"type": "n8n-nodes-base.supabase",
"position": [
-2496,
-768
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "patient_id",
"keyValue": "={{ $json.id }}",
"condition": "eq"
}
]
},
"tableId": "dental_appointments_canada",
"operation": "getAll",
"returnAll": true
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "b858e07e-c6fb-42ea-942d-3b52c257d11c",
"name": "Aggregate Patient Appointments",
"type": "n8n-nodes-base.aggregate",
"position": [
-2272,
-768
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData"
},
"typeVersion": 1
},
{
"id": "0b780901-6dc4-4a65-9b5e-4ecc9ae02727",
"name": "Send Summarized Appointment List",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-1824,
-768
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\n \"type\": \"conversation_initiation_client_data\",\n \"conversation_config_override\": {\n \"agent\": {},\n \"tts\": {}\n },\n \"custom_llm_extra_body\": {\n \"temperature\": 0.7,\n \"max_tokens\": 150\n },\n \"dynamic_variables\": {\n \"appointments_list\": {{ $json.data }} }\n}"
},
"typeVersion": 1.2
},
{
"id": "6247588f-5eb9-4e93-91e2-ffb92db674a0",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2544,
-560
],
"parameters": {
"color": 7,
"width": 1104,
"height": 448,
"content": "## Availability Search (AI-Generated Time Slots)\nThis section checks all existing appointments for the selected doctor and uses an LLM to generate a list of free 60-minute time slots between the requested start and end dates. The agent considers working hours and weekends, then returns the first available opening back to the caller."
},
"typeVersion": 1
},
{
"id": "2dc17dc7-c10f-4f49-a2fa-ca95fe7d342b",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2544,
-48
],
"parameters": {
"color": 7,
"width": 928,
"height": 336,
"content": "## Retrieve & Summarize Doctors\nThis section fetches all dental doctors from Supabase, aggregates and summarizes the data, and returns a structured list for the phone assistant. It handles fetching, compiling, and formatting the doctors\u2019 information into a clean response."
},
"typeVersion": 1
},
{
"id": "7cefd555-d28f-4f24-966f-79e5fe8fd0e3",
"name": "LLM Engine",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
-2496,
-256
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-4o",
"cachedResultName": "gpt-4o"
},
"options": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.2
},
{
"id": "f127ef89-4808-449a-8d63-0cfa0d9b5090",
"name": "Generate Availability With AI",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-2496,
-416
],
"parameters": {
"text": "=Check the start date ({{ $json.body.args.start }})\nto the end date ({{ $json.body.args.end }})\nand see which days the doctor_id ({{ $json.body.args.doctor_id }}) is available.\n(working hours are from 8:00 AM to 5:00 PM except Saturdays and Sundays)\nwhen checking for available times, consider the duration of appointments.\nfree time slots are 60 min long.\nexample:\nMonday 9:00 AM - 10:00 AM - 11:00 AM - ...\njust output the list of empty time slots\noutput = date and time",
"options": {},
"promptType": "define"
},
"typeVersion": 1.9
},
{
"id": "f0f7d22d-b801-428f-8a14-e7f87ca04923",
"name": "Send Availability Response",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-2176,
-416
],
"parameters": {
"options": {}
},
"typeVersion": 1.2
},
{
"id": "4d703012-e7d1-40ed-9b14-07eff57f540f",
"name": "Fetch Doctor\u2019s Appointments",
"type": "n8n-nodes-base.supabaseTool",
"position": [
-2352,
-256
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "doctor_id",
"keyValue": "={{ $json.body.args.doctor_id }}",
"condition": "eq"
}
]
},
"tableId": "dental_appointments_canada",
"operation": "getAll"
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "f69de5e9-d756-4ed7-bbb3-2c4ec18bd21f",
"name": "Fetch All Doctors",
"type": "n8n-nodes-base.supabase",
"position": [
-2480,
112
],
"parameters": {
"tableId": "dental_doctors_canada",
"operation": "getAll",
"returnAll": true
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "5871b5e1-7bb8-4e6b-a9e1-4c21943d1799",
"name": "Aggregate Doctors Data",
"type": "n8n-nodes-base.aggregate",
"position": [
-2304,
112
],
"parameters": {
"options": {},
"aggregate": "aggregateAllItemData"
},
"typeVersion": 1
},
{
"id": "25c68632-a4a5-422b-b92e-1fc2088887e1",
"name": "Summarize Doctors Data",
"type": "n8n-nodes-base.summarize",
"position": [
-2128,
112
],
"parameters": {
"options": {},
"fieldsToSplitBy": "data",
"fieldsToSummarize": {
"values": [
{
"field": "data",
"aggregation": "append"
}
]
}
},
"typeVersion": 1.1
},
{
"id": "5c3367b0-b17e-4065-8c00-0d5370b26015",
"name": "Send Doctors List",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-1936,
112
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\n \"type\": \"conversation_initiation_client_data\",\n \"conversation_config_override\": {\n \"agent\": {},\n \"tts\": {}\n },\n \"custom_llm_extra_body\": {\n \"temperature\": 0.7,\n \"max_tokens\": 150\n },\n \"dynamic_variables\": {\n \"doctors_list\": {{ $json.data }} }\n}"
},
"typeVersion": 1.2
},
{
"id": "7f40bdce-0c7f-4f9c-b86f-94fa1f8bea70",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2512,
-2208
],
"parameters": {
"color": 7,
"width": 624,
"height": 368,
"content": "## Reschedule \u2013 Fetch & Validate\nRetrieves the patient\u2019s current appointment, then checks whether the doctor is available at the new preferred date. This section ensures the system only proceeds to rescheduling if the requested time slot is free."
},
"typeVersion": 1
},
{
"id": "f468340d-116a-4688-ac05-f6a564693773",
"name": "Confirm Rescheduled Appointment",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-1232,
-1936
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "{\n \"type\": \"conversation_initiation_client_data\",\n \"conversation_config_override\": {\n \"agent\": {},\n \"tts\": {}\n },\n \"custom_llm_extra_body\": {\n \"temperature\": 0.7,\n \"max_tokens\": 150\n },\n \"dynamic_variables\": {\n \"Message\": \"Appointment rescheduled to {{ $('Switch').item.json.body.preferred_date }}\" }\n}\n\n\n\n"
},
"typeVersion": 1.2
},
{
"id": "2a034a07-8eab-4a98-9bd0-c1d5b270c159",
"name": "Update Appointment to New Slot",
"type": "n8n-nodes-base.supabase",
"position": [
-1456,
-1936
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "doctor_id",
"keyValue": "={{ $('Webhook').item.json.body.args.doctor_id }}",
"condition": "eq"
},
{
"keyName": "appointment_date",
"keyValue": "={{ $('Webhook').item.json.body.args.start }}",
"condition": "eq"
}
]
},
"tableId": "dental_appointments_canada",
"fieldsUi": {
"fieldValues": [
{
"fieldId": "appointment_date",
"fieldValue": "={{ $('Webhook').item.json.body.args.preferred_date }}"
}
]
},
"matchType": "allFilters",
"operation": "update"
},
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
},
"typeVersion": 1,
"alwaysOutputData": true
},
{
"id": "3900e990-1d0a-4e1a-b01d-de085a09de58",
"name": "Notify: Time Slot Taken",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-1632,
-2048
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "{\n \"type\": \"conversation_initiation_client_data\",\n \"conversation_config_override\": {\n \"agent\": {},\n \"tts\": {}\n },\n \"custom_llm_extra_body\": {\n \"temperature\": 0.7,\n \"max_tokens\": 150\n },\n \"dynamic_variables\": {\n \"Message\": \"That date is already booked.\" }\n}\n\n\n"
},
"typeVersion": 1.2
},
{
"id": "306c7964-8e1b-4b74-bc2c-0f6b7fe9e3ec",
"name": "Is Reschedule Possible?",
"type": "n8n-nodes-base.if",
"position": [
-1824,
-2032
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "a10944f0-0a90-4a49-8745-f472021a9339",
"operator": {
"type": "number",
"operation": "exists",
"singleValue": true
},
"leftValue": "={{ $json.id }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.2
},
{
"id": "1f71a805-2740-49ad-b83c-9ea3d3a9e33f",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1872,
-2192
],
"parameters": {
"color": 7,
"width": 848,
"height": 416,
"content": "## Reschedule \u2013 Decision & Update\nEvaluates whether the preferred date is free. If unavailable, sends a clear notification. If available, updates the appointment in Supabase and returns a confirmation message. This section ensures the reschedule flow cleanly handles both outcomes."
},
"typeVersion": 1
},
{
"id": "375fc43f-6e20-4235-91f4-28715dcaf9d8",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2528,
-1792
],
"parameters": {
"color": 7,
"width": 592,
"height": 352,
"content": "## Cancellation \u2013 Remove & Confirm\nDeletes the patient\u2019s appointment record from the database, then returns a confirmation message. This section handles the final step of the cancellation flow and ensures the user receives clear acknowledgment."
},
"typeVersion": 1
},
{
"id": "9edc5044-cfd6-4780-abb8-343c3ac6592e",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2512,
-1408
],
"parameters": {
"color": 7,
"width": 624,
"height": 432,
"content": "## Insurance Check \u2013 Validate & Respond\nEvaluates whether the patient\u2019s insurance record exists. If valid, returns an acceptance message; otherwise, notifies the user that the insurance is not accepted. This section handles the entire insurance-verification step."
},
"typeVersion": 1
}
],
"connections": {
"Switch": {
"main": [
[
{
"node": "Find Patient (Booking)",
"type": "main",
"index": 0
}
],
[
{
"node": "Find Patient (Reschedule)",
"type": "main",
"index": 0
}
],
[
{
"node": "Find Patient (Cancel)",
"type": "main",
"index": 0
}
],
[
{
"node": "Find Patient (List Appointments)",
"type": "main",
"index": 0
}
],
[
{
"node": "Find Insurance Record",
"type": "main",
"index": 0
}
],
[
{
"node": "Generate Availability With AI",
"type": "main",
"index": 0
}
],
[
{
"node": "Fetch All Doctors",
"type": "main",
"index": 0
}
]
]
},
"Webhook": {
"main": [
[
{
"node": "Switch",
"type": "main",
"index": 0
}
]
]
},
"LLM Engine": {
"ai_languageModel": [
[
{
"node": "Generate Availability With AI",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Fetch All Doctors": {
"main": [
[
{
"node": "Aggregate Doctors Data",
"type": "main",
"index": 0
}
]
]
},
"Create Appointment": {
"main": [
[
{
"node": "Get Existing Appointments",
"type": "main",
"index": 0
}
]
]
},
"Find Insurance Record": {
"main": [
[
{
"node": "Fetch Patient Appointments",
"type": "main",
"index": 0
}
]
]
},
"Find Patient (Cancel)": {
"main": [
[
{
"node": "Delete Appointment Record",
"type": "main",
"index": 0
}
]
]
},
"Aggregate Doctors Data": {
"main": [
[
{
"node": "Summarize Doctors Data",
"type": "main",
"index": 0
}
]
]
},
"Check Insurance Status": {
"main": [
[
{
"node": "Notify Insurance Accepted",
"type": "main",
"index": 0
}
],
[
{
"node": "Notify Insurance Rejected",
"type": "main",
"index": 0
}
]
]
},
"Find Patient (Booking)": {
"main": [
[
{
"node": "Check Time Availability",
"type": "main",
"index": 0
}
]
]
},
"Summarize Doctors Data": {
"main": [
[
{
"node": "Send Doctors List",
"type": "main",
"index": 0
}
]
]
},
"Check Time Availability": {
"main": [
[
{
"node": "Get Existing Appointments",
"type": "main",
"index": 0
}
],
[
{
"node": "Create Appointment",
"type": "main",
"index": 0
}
]
]
},
"Is Reschedule Possible?": {
"main": [
[
{
"node": "Notify: Time Slot Taken",
"type": "main",
"index": 0
}
],
[
{
"node": "Update Appointment to New Slot",
"type": "main",
"index": 0
}
]
]
},
"Time Slot Already Taken?": {
"main": [
[
{
"node": "Notify Appointment Conflict",
"type": "main",
"index": 0
}
],
[
{
"node": "Verify Patient Phone Number",
"type": "main",
"index": 0
}
]
]
},
"Create Appointment Record": {
"main": [
[
{
"node": "Notify Appointment Time",
"type": "main",
"index": 0
}
]
]
},
"Delete Appointment Record": {
"main": [
[
{
"node": "Notify Cancelation Success",
"type": "main",
"index": 0
}
]
]
},
"Find Patient (Reschedule)": {
"main": [
[
{
"node": "Fetch Patient's Current Appointment",
"type": "main",
"index": 0
}
]
]
},
"Get Existing Appointments": {
"main": [
[
{
"node": "Time Slot Already Taken?",
"type": "main",
"index": 0
}
]
]
},
"Fetch Patient Appointments": {
"main": [
[
{
"node": "Aggregate Patient Appointments",
"type": "main",
"index": 0
}
]
]
},
"Summarize Appointment Data": {
"main": [
[
{
"node": "Send Summarized Appointment List",
"type": "main",
"index": 0
}
]
]
},
"Verify Patient Phone Number": {
"main": [
[
{
"node": "Create Appointment Record",
"type": "main",
"index": 0
}
]
]
},
"Fetch Doctor\u2019s Appointments": {
"ai_tool": [
[
{
"node": "Generate Availability With AI",
"type": "ai_tool",
"index": 0
}
]
]
},
"Generate Availability With AI": {
"main": [
[
{
"node": "Send Availability Response",
"type": "main",
"index": 0
}
]
]
},
"Aggregate Patient Appointments": {
"main": [
[
{
"node": "Summarize Appointment Data",
"type": "main",
"index": 0
}
]
]
},
"Update Appointment to New Slot": {
"main": [
[
{
"node": "Confirm Rescheduled Appointment",
"type": "main",
"index": 0
}
]
]
},
"Find Patient (List Appointments)": {
"main": [
[
{
"node": "Check Insurance Status",
"type": "main",
"index": 0
}
]
]
},
"Fetch Patient's Current Appointment": {
"main": [
[
{
"node": "Check Doctor Availability for New Slot",
"type": "main",
"index": 0
}
]
]
},
"Check Doctor Availability for New Slot": {
"main": [
[
{
"node": "Is Reschedule Possible?",
"type": "main",
"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.
openAiApisupabaseApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow automates dental appointment management through a phone-based assistant. It listens for requests like booking, rescheduling, canceling, checking insurance, looking up appointments, and finding available time slots. Each request is processed through a Switch node…
Source: https://n8n.io/workflows/11463/ — 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.
Agent: IPTV (instance_e2165d22_1762376395079). Uses openAi, redis, supabase, httpRequest. Webhook trigger; 56 nodes.
'Elena AI' is a powerful n8n workflow that transforms your automation platform into a full-fledged, multi-agent AI hub. 🤖✨ By combining Redis state management with specialized “tool” sub-workflows, yo
⏺ 🚀 How it works
L&D_AgentsAI_ATIVO. Uses httpRequest, agent, googleCalendarTool, toolSerpApi. Webhook trigger; 93 nodes.
Flux. Uses lmChatOpenAi, agent, googleGemini, httpRequest. Webhook trigger; 67 nodes.