This workflow corresponds to n8n.io template #7249 — we link there as the canonical source.
This workflow follows the Emailsend → Google Sheets 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": "hWACWrSaTJyZTxzH",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Medical Equipment Maintenance Alert System",
"tags": [],
"nodes": [
{
"id": "661d56b2-1dba-47ab-91fa-56900b544b9b",
"name": "Daily Equipment Check (6 AM)",
"type": "n8n-nodes-base.cron",
"position": [
-1500,
140
],
"parameters": {},
"typeVersion": 1
},
{
"id": "14df28e8-686d-4c2a-8754-64e817e88941",
"name": "Read Equipment Data",
"type": "n8n-nodes-base.googleSheets",
"position": [
-1280,
140
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultName": "Equipment"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_GOOGLE_SHEET_ID"
},
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 4
},
{
"id": "bf61b7e2-79e9-4af7-b30b-f2aa260b51f1",
"name": "Process Equipment Alerts",
"type": "n8n-nodes-base.code",
"position": [
-1060,
140
],
"parameters": {
"jsCode": "const equipmentList = $input.all();\nconst alerts = [];\nconst today = new Date();\n\nfor (const equipment of equipmentList) {\n const equipData = equipment.json;\n \n // Skip header row\n if (equipData['Equipment ID'] === 'Equipment ID') continue;\n \n // Parse dates\n const lastMaintenance = new Date(equipData['Last Maintenance']);\n const lastCalibration = new Date(equipData['Last Calibration']);\n const warrantyExpiry = new Date(equipData['Warranty Expiry']);\n \n // Calculate days since last maintenance/calibration\n const daysSinceMaintenance = Math.floor((today - lastMaintenance) / (1000 * 60 * 60 * 24));\n const daysSinceCalibration = Math.floor((today - lastCalibration) / (1000 * 60 * 60 * 24));\n const daysToWarrantyExpiry = Math.floor((warrantyExpiry - today) / (1000 * 60 * 60 * 24));\n \n // Get maintenance and calibration intervals\n const maintenanceInterval = parseInt(equipData['Maintenance Interval (Days)']) || 90;\n const calibrationInterval = parseInt(equipData['Calibration Interval (Days)']) || 365;\n \n // Current usage hours\n const currentUsage = parseFloat(equipData['Current Usage Hours']) || 0;\n const maxUsage = parseFloat(equipData['Max Usage Hours']) || 8760; // Default 1 year\n const usagePercentage = (currentUsage / maxUsage) * 100;\n \n // Determine alert types and priority\n let alertTypes = [];\n let priority = 'Normal';\n let alertMessage = '';\n \n // Maintenance alerts\n if (daysSinceMaintenance >= maintenanceInterval) {\n alertTypes.push('OVERDUE_MAINTENANCE');\n priority = 'High';\n alertMessage += `\u26a0\ufe0f OVERDUE MAINTENANCE (${daysSinceMaintenance} days overdue)\\n`;\n } else if (daysSinceMaintenance >= maintenanceInterval - 7) {\n alertTypes.push('UPCOMING_MAINTENANCE');\n if (priority === 'Normal') priority = 'Medium';\n const daysLeft = maintenanceInterval - daysSinceMaintenance;\n alertMessage += `\ud83d\udd27 Maintenance due in ${daysLeft} days\\n`;\n }\n \n // Calibration alerts\n if (daysSinceCalibration >= calibrationInterval) {\n alertTypes.push('OVERDUE_CALIBRATION');\n priority = 'High';\n alertMessage += `\u26a0\ufe0f OVERDUE CALIBRATION (${daysSinceCalibration} days overdue)\\n`;\n } else if (daysSinceCalibration >= calibrationInterval - 14) {\n alertTypes.push('UPCOMING_CALIBRATION');\n if (priority === 'Normal') priority = 'Medium';\n const daysLeft = calibrationInterval - daysSinceCalibration;\n alertMessage += `\ud83c\udfaf Calibration due in ${daysLeft} days\\n`;\n }\n \n // Usage alerts\n if (usagePercentage >= 95) {\n alertTypes.push('CRITICAL_USAGE');\n priority = 'Critical';\n alertMessage += `\ud83d\udea8 CRITICAL USAGE: ${usagePercentage.toFixed(1)}% of max hours\\n`;\n } else if (usagePercentage >= 80) {\n alertTypes.push('HIGH_USAGE');\n if (priority === 'Normal' || priority === 'Medium') priority = 'Medium';\n alertMessage += `\ud83d\udcca High usage: ${usagePercentage.toFixed(1)}% of max hours\\n`;\n }\n \n // Warranty alerts\n if (daysToWarrantyExpiry <= 0) {\n alertTypes.push('WARRANTY_EXPIRED');\n priority = 'High';\n alertMessage += `\u274c WARRANTY EXPIRED (${Math.abs(daysToWarrantyExpiry)} days ago)\\n`;\n } else if (daysToWarrantyExpiry <= 30) {\n alertTypes.push('WARRANTY_EXPIRING');\n if (priority === 'Normal') priority = 'Medium';\n alertMessage += `\u23f0 Warranty expires in ${daysToWarrantyExpiry} days\\n`;\n }\n \n // Status-based alerts\n if (equipData['Status'] === 'Out of Service') {\n alertTypes.push('OUT_OF_SERVICE');\n priority = 'Critical';\n alertMessage += `\ud83d\udd34 EQUIPMENT OUT OF SERVICE\\n`;\n } else if (equipData['Status'] === 'Needs Repair') {\n alertTypes.push('NEEDS_REPAIR');\n priority = 'High';\n alertMessage += `\ud83d\udd27 EQUIPMENT NEEDS REPAIR\\n`;\n }\n \n // Only create alert if there are issues\n if (alertTypes.length > 0) {\n const alert = {\n equipmentId: equipData['Equipment ID'],\n equipmentName: equipData['Equipment Name'],\n location: equipData['Location'],\n department: equipData['Department'],\n status: equipData['Status'],\n alertTypes: alertTypes,\n priority: priority,\n alertMessage: alertMessage.trim(),\n technicianEmail: equipData['Technician Email'],\n technicianWhatsApp: equipData['Technician WhatsApp'],\n supervisorEmail: equipData['Supervisor Email'],\n daysSinceMaintenance: daysSinceMaintenance,\n daysSinceCalibration: daysSinceCalibration,\n usagePercentage: usagePercentage.toFixed(1),\n daysToWarrantyExpiry: daysToWarrantyExpiry,\n manufacturer: equipData['Manufacturer'],\n model: equipData['Model'],\n serialNumber: equipData['Serial Number'],\n currentDate: today.toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n })\n };\n \n // Create detailed email content\n alert.emailSubject = `\ud83c\udfe5 Equipment Alert: ${alert.equipmentName} (${alert.equipmentId}) - ${priority} Priority`;\n \n alert.emailBody = `\nDear Maintenance Team,\n\nEquipment Maintenance Alert - ${alert.currentDate}\n\n\ud83c\udfe5 EQUIPMENT DETAILS:\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\nEquipment ID: ${alert.equipmentId}\nName: ${alert.equipmentName}\nLocation: ${alert.location}\nDepartment: ${alert.department}\nStatus: ${alert.status}\nPriority: ${priority}\n\n\ud83d\udccb EQUIPMENT SPECIFICATIONS:\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\nManufacturer: ${alert.manufacturer}\nModel: ${alert.model}\nSerial Number: ${alert.serialNumber}\n\n\u26a0\ufe0f ALERT DETAILS:\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n${alert.alertMessage}\n\n\ud83d\udcca MAINTENANCE STATUS:\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\u2022 Days since last maintenance: ${alert.daysSinceMaintenance}\n\u2022 Days since last calibration: ${alert.daysSinceCalibration}\n\u2022 Current usage: ${alert.usagePercentage}% of maximum\n\u2022 Warranty expires in: ${alert.daysToWarrantyExpiry} days\n\n\ud83d\udd27 ACTION REQUIRED:\nPlease schedule appropriate maintenance/calibration as indicated above.\nContact supervisor if equipment is critical for patient care.\n\nBest regards,\nEquipment Management System\n `;\n \n // Create WhatsApp message\n alert.whatsappMessage = `\ud83c\udfe5 *EQUIPMENT ALERT*\\n\\n\ud83d\udd27 *${alert.equipmentName}*\\n\ud83d\udccd Location: ${alert.location}\\n\ud83d\udea8 Priority: ${priority}\\n\\n${alert.alertMessage.replace(/\\n/g, '\\n')}\\n\\n\ud83d\udcca Usage: ${alert.usagePercentage}%\\n\ud83d\udcc5 Alert Date: ${alert.currentDate}`;\n \n alerts.push(alert);\n }\n}\n\n// Sort alerts by priority (Critical > High > Medium > Normal)\nconst priorityOrder = { 'Critical': 1, 'High': 2, 'Medium': 3, 'Normal': 4 };\nalerts.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);\n\nreturn alerts.map(alert => ({ json: alert }));"
},
"typeVersion": 2
},
{
"id": "cf09a396-a848-4014-8fd2-513d36832831",
"name": "Filter Equipment with Alerts",
"type": "n8n-nodes-base.filter",
"position": [
-620,
140
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "has-alerts",
"operator": {
"type": "number",
"operation": "gt"
},
"leftValue": "={{ $json.alertTypes.length }}",
"rightValue": 0
}
]
}
},
"typeVersion": 2
},
{
"id": "b176f175-4868-438d-b16b-1334cd5f1e69",
"name": "Send Technician Email",
"type": "n8n-nodes-base.emailSend",
"position": [
-400,
-260
],
"parameters": {
"options": {},
"subject": "={{ $json.emailSubject }}",
"toEmail": "={{ $json.technicianEmail }}",
"fromEmail": "user@example.com"
},
"credentials": {
"smtp": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "eb4277e9-7e3c-4dd6-83fe-43b823527b47",
"name": "Filter Critical Equipment",
"type": "n8n-nodes-base.filter",
"position": [
-400,
-60
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "or",
"conditions": [
{
"id": "critical-priority",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.priority }}",
"rightValue": "Critical"
}
]
}
},
"typeVersion": 2
},
{
"id": "4ecad11b-1b12-4d54-b44c-17b3b6879c59",
"name": "Send Critical Alert to Supervisors",
"type": "n8n-nodes-base.emailSend",
"position": [
-180,
-160
],
"parameters": {
"options": {},
"subject": "\ud83d\udea8 CRITICAL EQUIPMENT ALERT - {{ $json.equipmentName }}",
"toEmail": "user@example.com,user@example.com",
"fromEmail": "user@example.com"
},
"credentials": {
"smtp": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "b3f1bdc8-25c1-4bfa-88ab-c577a01836b0",
"name": "Log Maintenance Alerts",
"type": "n8n-nodes-base.googleSheets",
"position": [
-400,
340
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=1",
"cachedResultName": "Alert_Log"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_GOOGLE_SHEET_ID"
},
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 4
},
{
"id": "a35161f0-1747-4cf1-a189-3d255053468b",
"name": "Filter Overdue Equipment",
"type": "n8n-nodes-base.filter",
"position": [
-400,
540
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "overdue-maintenance",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $json.alertTypes.includes('OVERDUE_MAINTENANCE') || $json.alertTypes.includes('OVERDUE_CALIBRATION') }}",
"rightValue": true
}
]
}
},
"typeVersion": 2
},
{
"id": "d4e4bd40-5570-4463-a094-e5f54d4f58de",
"name": "Update Equipment Status",
"type": "n8n-nodes-base.googleSheets",
"position": [
-180,
540
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultName": "Equipment"
},
"documentId": {
"__rl": true,
"mode": "id",
"value": "YOUR_GOOGLE_SHEET_ID"
},
"authentication": "serviceAccount"
},
"credentials": {
"googleApi": {
"name": "<your credential>"
}
},
"typeVersion": 4
},
{
"id": "aea195b9-fd94-4910-b249-352d629f9bab",
"name": "Tack Break For 5 Sec",
"type": "n8n-nodes-base.wait",
"position": [
-840,
140
],
"parameters": {},
"typeVersion": 1.1
},
{
"id": "fda3f3f5-cf5d-491c-a73e-2600a2277567",
"name": "Send message",
"type": "n8n-nodes-base.whatsApp",
"position": [
-400,
140
],
"parameters": {
"textBody": "=\ud83d\udea8 EQUIPMENT ALERT - {{ $json.equipmentName }}",
"operation": "send",
"phoneNumberId": "=+919876587688",
"additionalFields": {},
"recipientPhoneNumber": "+1234567890"
},
"credentials": {
"whatsAppApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "0f690c13-e2b7-4c0d-8c06-d4b679a33f66",
"name": "Send Critical Alert Massage",
"type": "n8n-nodes-base.whatsApp",
"position": [
-180,
140
],
"parameters": {
"textBody": "=\ud83d\udea8 CRITICAL EQUIPMENT ALERT - {{ $json.equipmentName }}",
"operation": "send",
"phoneNumberId": "=+919876587688",
"additionalFields": {},
"recipientPhoneNumber": "+1234567890"
},
"credentials": {
"whatsAppApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
},
{
"id": "b9eac557-be5c-4482-bc22-1871cf5622aa",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1360,
-280
],
"parameters": {
"color": 6,
"width": 560,
"height": 320,
"content": "## How It Works\n- **Daily Equipment Check (6 AM)** - Triggers the workflow.\n- **Read Equipment Data** - Fetches data from Google Sheet.\n- **Process Equipment Alerts** - Identifies maintenance needs.\n- **Task Break For 5 Sec** - Adds a delay for processing.\n- **Filter Equipment with Alerts** - Filters equipment needing attention.\n- **Send Technician Email** - Notifies technicians via email.\n- **Send Message (message: send)** - Sends WhatsApp alerts to technicians.\n- **Send Critical Alert to Supervisors** - Escalates critical issues via email and WhatsApp.\n- **Filter Overdue Equipment** - Identifies overdue maintenance.\n- **Update Equipment Status** - Updates sheet with new statuses.\n- **Log Maintenance Alerts** - Logs alerts in the sheet."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "763d56dd-6308-4e58-a96a-088242a0e354",
"connections": {
"Read Equipment Data": {
"main": [
[
{
"node": "Process Equipment Alerts",
"type": "main",
"index": 0
}
]
]
},
"Tack Break For 5 Sec": {
"main": [
[
{
"node": "Filter Equipment with Alerts",
"type": "main",
"index": 0
}
]
]
},
"Filter Overdue Equipment": {
"main": [
[
{
"node": "Update Equipment Status",
"type": "main",
"index": 0
}
]
]
},
"Process Equipment Alerts": {
"main": [
[
{
"node": "Tack Break For 5 Sec",
"type": "main",
"index": 0
}
]
]
},
"Filter Critical Equipment": {
"main": [
[
{
"node": "Send Critical Alert to Supervisors",
"type": "main",
"index": 0
},
{
"node": "Send Critical Alert Massage",
"type": "main",
"index": 0
}
]
]
},
"Daily Equipment Check (6 AM)": {
"main": [
[
{
"node": "Read Equipment Data",
"type": "main",
"index": 0
}
]
]
},
"Filter Equipment with Alerts": {
"main": [
[
{
"node": "Send Technician Email",
"type": "main",
"index": 0
},
{
"node": "Filter Critical Equipment",
"type": "main",
"index": 0
},
{
"node": "Log Maintenance Alerts",
"type": "main",
"index": 0
},
{
"node": "Filter Overdue Equipment",
"type": "main",
"index": 0
},
{
"node": "Send message",
"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.
googleApismtpwhatsAppApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This n8n workflow monitors medical equipment usage by reading data daily at 6 AM from a Google Sheet, processes alerts for maintenance or calibration, and sends notifications to technicians and supervisors. Runs daily at 6 AM via cron trigger. Requires Google Sheet with…
Source: https://n8n.io/workflows/7249/ — 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.
⚠️ Heads up: this is satire. The "Hell Yeah!" workflow is a parody of "automate your whole life with AI agents" grindset content. The API endpoints are fictional and the function nodes are illustrativ
Enhance financial oversight with this automated n8n workflow. Triggered every 5 minutes, it fetches real-time bank transactions via an API, enriches and transforms the data, and applies smart logic to
This workflow automates competitive price intelligence using Bright Data's enterprise web scraping API. On a scheduled basis (default: daily at 9 AM), the system loops through configured competitor pr
SEO managers, content marketers, bloggers, and growth teams who want to automatically catch declining content performance before it's too late — without manually checking Google Search Console every w
Automate tax deadline monitoring with AI-powered insights. This workflow checks your tax calendar daily at 8 AM, uses GPT-4 to analyze upcoming deadlines across multiple jurisdictions, detects overdue