This workflow corresponds to n8n.io template #16453 — we link there as the canonical source.
This workflow follows the Error Trigger → 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": "hqQSZC40uc2RLEQR",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Appointment Reminder Sender",
"tags": [],
"nodes": [
{
"id": "f44f1348-3658-4945-b370-70de7e3e8f77",
"name": "Every Morning at 9",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
2128,
1584
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 9
}
]
}
},
"typeVersion": 1.2
},
{
"id": "abca920b-ed55-46b5-b957-93d1cf1e5b12",
"name": "Get Appointments",
"type": "n8n-nodes-base.googleSheets",
"position": [
2416,
1584
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/12lsTQ_B6G_39C0JwXZB_N-9Gk0STtrvZQ9bMAyX7ELo/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "12lsTQ_B6G_39C0JwXZB_N-9Gk0STtrvZQ9bMAyX7ELo",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/12lsTQ_B6G_39C0JwXZB_N-9Gk0STtrvZQ9bMAyX7ELo/edit?usp=drivesdk",
"cachedResultName": "New Form_fashinbrand"
}
},
"typeVersion": 4.5
},
{
"id": "2ef514e2-a4f5-4327-b1a4-dae39c124bfc",
"name": "Filter For Tomorrow",
"type": "n8n-nodes-base.code",
"position": [
2672,
1584
],
"parameters": {
"jsCode": "const tomorrow = new Date();\ntomorrow.setDate(tomorrow.getDate() + 1);\nconst target = tomorrow.toISOString().slice(0, 10);\nreturn $input.all().filter(i => String(i.json['Date']).slice(0, 10) === target);"
},
"typeVersion": 2
},
{
"id": "93f1d14c-00fa-4914-973b-52b034d8fb95",
"name": "Filter Out Cancelled",
"type": "n8n-nodes-base.code",
"position": [
2928,
1584
],
"parameters": {
"jsCode": "return $input.all().filter(i => String(i.json['Status'] || '').toLowerCase() !== 'cancelled');"
},
"typeVersion": 2
},
{
"id": "be35c346-db7b-4049-9f05-1092fcc34538",
"name": "Has Valid Email",
"type": "n8n-nodes-base.if",
"position": [
3184,
1584
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "e1",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{$json['Email']}}",
"rightValue": ""
}
]
}
},
"typeVersion": 2
},
{
"id": "3911748e-9bfc-48e0-95ee-d1a210b73ee0",
"name": "Flag Missing Contact Info",
"type": "n8n-nodes-base.set",
"position": [
3456,
1632
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "f1",
"name": "issue",
"type": "string",
"value": "Missing email \u2014 call patient directly"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "68dc533e-3512-4b85-bf32-143816e434b8",
"name": "Alert Front Desk Missing Info",
"type": "n8n-nodes-base.slack",
"position": [
3712,
1632
],
"parameters": {
"text": "No email on file for {{$json['Patient']}}'s appointment on {{$json['Date']}} at {{$json['Time']}}. Please call to confirm.",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "C0B1SAX5L3W",
"cachedResultName": "new_ai-updates"
},
"otherOptions": {},
"authentication": "oAuth2"
},
"typeVersion": 2.3
},
{
"id": "fe664c3c-06cf-4ecb-be3a-b4d02bf60e11",
"name": "Send Reminder Email",
"type": "n8n-nodes-base.gmail",
"position": [
3440,
1440
],
"parameters": {
"sendTo": "={{$json['Email']}}",
"message": "Hi {{$json['Patient']}}, this is a reminder of your appointment on {{$json['Date']}} at {{$json['Time']}}. Please reply if you need to reschedule.",
"options": {},
"subject": "Appointment reminder for {{$json['Date']}}"
},
"typeVersion": 2.1
},
{
"id": "426aeb36-e68b-4809-837b-3781bbbabf5b",
"name": "Mark As Reminded",
"type": "n8n-nodes-base.googleSheets",
"position": [
3696,
1440
],
"parameters": {
"columns": {
"value": {
"Email": "={{ $json['Email'] }}",
"Status": "Reminded"
},
"schema": [
{
"id": "Email",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Status",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Status",
"defaultMatch": false,
"canBeUsedToMatch": false
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"Email"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/12lsTQ_B6G_39C0JwXZB_N-9Gk0STtrvZQ9bMAyX7ELo/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "12lsTQ_B6G_39C0JwXZB_N-9Gk0STtrvZQ9bMAyX7ELo",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/12lsTQ_B6G_39C0JwXZB_N-9Gk0STtrvZQ9bMAyX7ELo/edit?usp=drivesdk",
"cachedResultName": "New Form_fashinbrand"
}
},
"typeVersion": 4.5
},
{
"id": "7e213592-d57e-4672-86e8-cecc385b18f9",
"name": "Build Summary",
"type": "n8n-nodes-base.code",
"position": [
3296,
2320
],
"parameters": {
"jsCode": "const count = $input.all().length;\nreturn [{ json: { count, date: new Date(Date.now() + 86400000).toISOString().slice(0,10) } }];"
},
"typeVersion": 2
},
{
"id": "0becdb88-6b4d-4aaf-a846-2ab692462960",
"name": "Front Desk Summary",
"type": "n8n-nodes-base.slack",
"position": [
3552,
2320
],
"parameters": {
"text": "Reminders sent for {{$json.count}} appointments scheduled on {{$json.date}}.",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "C0B1SAX5L3W",
"cachedResultName": "new_ai-updates"
},
"otherOptions": {},
"authentication": "oAuth2"
},
"typeVersion": 2.3
},
{
"id": "30d7c1f1-cd7b-40aa-bf02-67083afade60",
"name": "Two Hour Reminder Check",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
2128,
2384
],
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 30
}
]
}
},
"typeVersion": 1.2
},
{
"id": "f47caca1-b488-4eac-b0c4-5edf47294f2d",
"name": "Get Today Appointments",
"type": "n8n-nodes-base.googleSheets",
"position": [
2416,
2384
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/12lsTQ_B6G_39C0JwXZB_N-9Gk0STtrvZQ9bMAyX7ELo/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "12lsTQ_B6G_39C0JwXZB_N-9Gk0STtrvZQ9bMAyX7ELo",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/12lsTQ_B6G_39C0JwXZB_N-9Gk0STtrvZQ9bMAyX7ELo/edit?usp=drivesdk",
"cachedResultName": "New Form_fashinbrand"
}
},
"typeVersion": 4.5
},
{
"id": "4955a1d2-844d-4102-a451-e3dad7e49337",
"name": "Filter Next Two Hours",
"type": "n8n-nodes-base.code",
"position": [
2672,
2384
],
"parameters": {
"jsCode": "const now = new Date();\nconst today = now.toISOString().slice(0, 10);\nreturn $input.all().filter(i => {\n const status = String(i.json['Status'] || '').toLowerCase();\n if (status === 'cancelled') return false;\n if (String(i.json['Date']).slice(0, 10) !== today) return false;\n const apptTime = new Date(`${today}T${String(i.json['Time']).padStart(5, '0')}:00`);\n const diffMins = (apptTime.getTime() - now.getTime()) / 60000;\n return diffMins > 0 && diffMins <= 120;\n});"
},
"typeVersion": 2
},
{
"id": "e4de2b86-c040-4b76-bc92-556a4f7426e3",
"name": "Send Final Reminder",
"type": "n8n-nodes-base.gmail",
"position": [
2928,
2384
],
"parameters": {
"sendTo": "={{$json['Email']}}",
"message": "Hi {{$json['Patient']}}, your appointment starts soon today at {{$json['Time']}}. We look forward to seeing you.",
"options": {},
"subject": "Your appointment starts soon"
},
"typeVersion": 2.1
},
{
"id": "c92b1633-89fa-42ba-ae93-7cdcbe8bf7bc",
"name": "Workflow Error Trigger",
"type": "n8n-nodes-base.errorTrigger",
"position": [
3280,
2576
],
"parameters": {},
"typeVersion": 1
},
{
"id": "a5bd6949-e07b-4fd2-9a1d-8e322edba442",
"name": "Alert Front Desk on Workflow Failure",
"type": "n8n-nodes-base.slack",
"position": [
3568,
2576
],
"parameters": {
"text": "Appointment Reminder Sender workflow failed: {{$json.execution.error.message}}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "C0B1SAX5L3W",
"cachedResultName": "new_ai-updates"
},
"otherOptions": {},
"authentication": "oAuth2"
},
"typeVersion": 2.3
},
{
"id": "57b74a56-2351-4d82-a969-b9e540dab024",
"name": "Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
1328,
1184
],
"parameters": {
"color": 7,
"width": 580,
"height": 1192,
"content": "## \ud83e\ude7a Appointment Reminder Sender\n\nEvery morning it reads your appointment sheet, finds tomorrow's bookings, skips cancellations, emails each patient a reminder, flags anyone missing contact info for a phone call instead, and posts a summary to your front desk channel. A second check throughout the day sends a final reminder two hours before same-day appointments, and a Slack alert fires if anything in the workflow fails.\n\n**Perfect for:** clinics, dental practices and small healthcare offices.\n\n---\n\n## How it works\n\n1. **Every Morning at 9** \u2014 schedule trigger that runs once daily.\n2. **Get Appointments** \u2014 reads all rows from your appointments sheet.\n3. **Filter For Tomorrow** \u2014 keeps only appointments dated for the next day.\n4. **Filter Out Cancelled** \u2014 drops any appointment marked Cancelled.\n5. **Has Valid Email** \u2014 branches on whether the patient has an email on file.\n6. **Send Reminder Email** / **Mark As Reminded** \u2014 *(has email)* emails the reminder, then marks the row as reminded so a retry never double-sends.\n7. **Flag Missing Contact Info** / **Alert Front Desk Missing Info** \u2014 *(no email)* tells front desk to call this patient instead.\n8. **Build Summary** / **Front Desk Summary** \u2014 counts how many reminders went out and posts the total to your team channel.\n9. **Two Hour Reminder Check** \u2014 *(second touchpoint)* runs every 30 minutes through the day.\n10. **Get Today Appointments** / **Filter Next Two Hours** \u2014 finds today's appointments starting within the next two hours.\n11. **Send Final Reminder** \u2014 *(optional path)* sends a same-day, starting-soon reminder.\n12. **Workflow Error Trigger** / **Alert Front Desk on Workflow Failure** \u2014 *(error path)* posts to Slack if any step in this workflow throws an error.\n\n---\n\n## Setup (~15 min)\n\n1. **Google Sheets** \u2014 connect your account and sheet id in *Get Appointments*, *Mark As Reminded*, and *Get Today Appointments*. Use columns Patient, Email, Date, Time, and add a Status column for Cancelled/Reminded.\n2. **Gmail** \u2014 connect Gmail in *Send Reminder Email* and *Send Final Reminder* and adjust the wording.\n3. **Slack** \u2014 connect Slack and set the channel in *Front Desk Summary*, *Alert Front Desk Missing Info*, and *Alert Front Desk on Workflow Failure*.\n> Make sure your Date column uses a format the *Filter For Tomorrow* code can read, such as YYYY-MM-DD, and Time uses 24-hour HH:MM."
},
"typeVersion": 1
},
{
"id": "240e9ab0-f629-4285-8f64-ea0c48c7bd0c",
"name": "1\ufe0f\u20e3 Daily Fetch",
"type": "n8n-nodes-base.stickyNote",
"position": [
2048,
1424
],
"parameters": {
"color": 5,
"width": 1040,
"height": 400,
"content": "## 1\ufe0f\u20e3 Daily Fetch\n\n**Every Morning at 9** triggers the run, **Get Appointments** pulls the full appointment list from your sheet, **Filter For Tomorrow** narrows it to next day bookings, and **Filter Out Cancelled** drops anything marked Cancelled."
},
"typeVersion": 1
},
{
"id": "74c03a5c-910a-4e04-a6c7-7b1cc80acd51",
"name": "2\ufe0f\u20e3 Reminders & Exceptions",
"type": "n8n-nodes-base.stickyNote",
"position": [
3120,
1248
],
"parameters": {
"color": 3,
"width": 880,
"height": 816,
"content": "## 2\ufe0f\u20e3 Reminders & Exceptions\n\n**Has Valid Email** checks each appointment for contact info. **Send Reminder Email** and **Mark As Reminded** handle the normal case, while **Flag Missing Contact Info** and **Alert Front Desk Missing Info** make sure anyone without an email still gets a human follow-up instead of being silently skipped."
},
"typeVersion": 1
},
{
"id": "66bad02c-5d47-4c61-9141-341852cc2332",
"name": "3\ufe0f\u20e3 Same-Day Reminder",
"type": "n8n-nodes-base.stickyNote",
"position": [
2048,
2208
],
"parameters": {
"color": 6,
"width": 1056,
"height": 496,
"content": "## 3\ufe0f\u20e3 Same-Day Reminder\n\n**Two Hour Reminder Check** runs throughout the day, **Get Today Appointments** and **Filter Next Two Hours** find bookings starting soon, and **Send Final Reminder** gives patients a last-minute nudge."
},
"typeVersion": 1
},
{
"id": "a59a7a33-65a4-43a7-a93f-28347cdb29e7",
"name": "4\ufe0f\u20e3 Summary & Error Handling",
"type": "n8n-nodes-base.stickyNote",
"position": [
3232,
2080
],
"parameters": {
"color": 4,
"width": 880,
"height": 664,
"content": "## 4\ufe0f\u20e3 Summary & Error Handling\n\n**Build Summary** counts how many appointments were processed and **Front Desk Summary** posts the total to your staff channel. **Workflow Error Trigger** and **Alert Front Desk on Workflow Failure** catch and surface any failure in this workflow."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "c86b55b5-e257-4969-9fa0-5fb0aaf722d2",
"nodeGroups": [],
"connections": {
"Build Summary": {
"main": [
[
{
"node": "Front Desk Summary",
"type": "main",
"index": 0
}
]
]
},
"Has Valid Email": {
"main": [
[
{
"node": "Send Reminder Email",
"type": "main",
"index": 0
}
],
[
{
"node": "Flag Missing Contact Info",
"type": "main",
"index": 0
}
]
]
},
"Get Appointments": {
"main": [
[
{
"node": "Filter For Tomorrow",
"type": "main",
"index": 0
}
]
]
},
"Every Morning at 9": {
"main": [
[
{
"node": "Get Appointments",
"type": "main",
"index": 0
}
]
]
},
"Filter For Tomorrow": {
"main": [
[
{
"node": "Filter Out Cancelled",
"type": "main",
"index": 0
}
]
]
},
"Send Reminder Email": {
"main": [
[
{
"node": "Mark As Reminded",
"type": "main",
"index": 0
}
]
]
},
"Filter Out Cancelled": {
"main": [
[
{
"node": "Has Valid Email",
"type": "main",
"index": 0
},
{
"node": "Build Summary",
"type": "main",
"index": 0
}
]
]
},
"Filter Next Two Hours": {
"main": [
[
{
"node": "Send Final Reminder",
"type": "main",
"index": 0
}
]
]
},
"Get Today Appointments": {
"main": [
[
{
"node": "Filter Next Two Hours",
"type": "main",
"index": 0
}
]
]
},
"Workflow Error Trigger": {
"main": [
[
{
"node": "Alert Front Desk on Workflow Failure",
"type": "main",
"index": 0
}
]
]
},
"Two Hour Reminder Check": {
"main": [
[
{
"node": "Get Today Appointments",
"type": "main",
"index": 0
}
]
]
},
"Flag Missing Contact Info": {
"main": [
[
{
"node": "Alert Front Desk Missing Info",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow runs daily at 9:00, reads appointments from Google Sheets, emails next-day patients reminders via Gmail, and posts a reminder count to a Slack channel. Runs every day at 9:00 using a schedule trigger. Reads all appointment rows from a specified Google Sheets…
Source: https://n8n.io/workflows/16453/ — 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.
Automatically identify clients who haven’t been contacted in 14+ days and re-engage them with personalized Gmail follow-up emails, Google Sheets tracking, and Slack notifications for account managers.
This workflow runs daily to read chronic care vitals from Google Sheets, merge them into a per-patient record, score risk using AHA/ADA threshold rules, and notify clinicians via Telegram and Gmail wh
Streamline client retention and contract renewals by automatically identifying expiring accounts, sending personalized reminder emails, and notifying account managers through Slack. This workflow ensu
This powerful n8n workflow automatically processes, categorizes, and organizes your Gmail inbox using customizable rules stored in Google Sheets. Say goodbye to manual email sorting and hello to a per
This workflow runs daily to pull cloud spend from a billing API, compare it to a Google Sheets rolling baseline, and alert on cost spikes by creating a Jira incident, posting to Slack, emailing Financ