AutomationFlowsEmail & Gmail › Triage Chronic Care Vitals with Google Sheets, Gmail, Telegram and Slack

Triage Chronic Care Vitals with Google Sheets, Gmail, Telegram and Slack

ByRahul Joshi @rahul08 on n8n.io

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 when escalation is needed, while logging all results back to Google Sheets. Runs…

Event trigger★★★★☆ complexity26 nodesError TriggerSlackGoogle SheetsTelegramGmail
Email & Gmail Trigger: Event Nodes: 26 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #16604 — 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 →

Download .json
{
  "id": "EorVl6kyafaJeRF0",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Daily Chronic Care Monitoring with AI Risk Assessment",
  "tags": [],
  "nodes": [
    {
      "id": "8b6b5ee3-8938-45f2-ad49-256bc7efa995",
      "name": "On Workflow Error",
      "type": "n8n-nodes-base.errorTrigger",
      "position": [
        -224,
        1440
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "c439f5de-a13b-4000-ac95-ab999008da2b",
      "name": "Slack: Error Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        32,
        1440
      ],
      "parameters": {
        "text": "=Error occured ",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C0AN1UGL0RM",
          "cachedResultName": "all-n8n-automations"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "3f9d0107-9036-4047-a0fa-63d3aefb92e5",
      "name": "Section: Error Handler",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -304,
        1296
      ],
      "parameters": {
        "color": 7,
        "width": 492,
        "height": 328,
        "content": "## \u26a0\ufe0f Error Handler\nCatches any failure across the entire workflow and immediately sends a Slack alert to the ops channel. Wire this to every sub-workflow or critical node to ensure no silent failures."
      },
      "typeVersion": 1
    },
    {
      "id": "043f497f-016a-4cb3-82ca-3a95a3bc7360",
      "name": "\ud83d\udccb Workflow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -848,
        -128
      ],
      "parameters": {
        "width": 752,
        "height": 480,
        "content": "## \ud83c\udfe5 Chronic Care Daily Monitoring Agent\n\n### How it works\nEvery day at 6 AM, this workflow pulls patient vitals from three Google Sheets tabs \u2014 blood pressure, glucose, and activity/sleep. It merges all readings into one record per patient, then runs a rule-based risk scoring engine (built on AHA and ADA clinical guidelines) to detect anomalies like hypertensive crises, hypoglycaemia, or severe sleep deprivation.\n\nPatients flagged as HIGH or CRITICAL risk trigger a clinical intervention plan, a Telegram alert to the doctor, and a detailed email summary. Low-risk patients receive a routine daily update by email. Every result \u2014 escalated or not \u2014 is logged back to Google Sheets for audit and trend tracking.\n\n### Setup steps\n1. Connect **Google Sheets OAuth2** and point all sheet nodes to your own spreadsheet ID\n2. Set up a **Telegram Bot** via @BotFather and update the doctor's chat ID in the alert node\n3. Connect **Gmail OAuth2** and replace placeholder emails with your clinic's doctor address\n4. Ensure your Google Sheet has tabs: `BloodPressure`, `Glucose`, `Activity`, `PatientMaster`, and `DailyLog`\n5. Set the Schedule Trigger to your preferred daily run time (default: every 24 hours)\n6. Run once in test mode with sample data before activating"
      },
      "typeVersion": 1
    },
    {
      "id": "e82131d8-7e23-4d57-9eed-a2e8dc216485",
      "name": "Section: Trigger",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        64,
        544
      ],
      "parameters": {
        "color": 7,
        "width": 316,
        "height": 380,
        "content": "## \u23f0 Scheduled Trigger\nFires once every 24 hours to kick off the daily monitoring cycle. Adjust the interval or use a cron expression to set a specific time like 6 AM."
      },
      "typeVersion": 1
    },
    {
      "id": "79d00aea-e66e-4c15-855e-3dd366207072",
      "name": "Section: Vitals Ingestion",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        400,
        80
      ],
      "parameters": {
        "color": 7,
        "width": 484,
        "height": 1328,
        "content": "## \ud83d\udce5 Vitals Data Ingestion\nReads today's rows from three separate sheet tabs in parallel \u2014 blood pressure, glucose, and activity/sleep. All four streams (including patient master) merge into a single pipeline before processing."
      },
      "typeVersion": 1
    },
    {
      "id": "7979aba3-101f-49a9-87c6-66f01c3f1d63",
      "name": "Section: Merge & Scoring",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        912,
        464
      ],
      "parameters": {
        "color": 7,
        "width": 900,
        "height": 576,
        "content": "## \ud83d\udd04 Merge & Risk Scoring\nCombines all readings into one structured record per patient, then scores each one against clinical thresholds. Scores \u2265 70 or any critical anomaly trigger escalation; everything else takes the routine path."
      },
      "typeVersion": 1
    },
    {
      "id": "09f5f14f-5af6-43a7-b516-04d9d5f6ae8b",
      "name": "Section: Escalation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1872,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 1220,
        "height": 816,
        "content": "## \ud83d\udea8 Escalation \u2014 High/Critical Risk\nGenerates a prioritised intervention plan, then fires both a Telegram message to the doctor and a detailed HTML email. Both outputs merge before writing the escalated record to Google Sheets."
      },
      "typeVersion": 1
    },
    {
      "id": "e4b97035-6092-4882-abcf-380a12878b93",
      "name": "Section: Routine Logging",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1936,
        1040
      ],
      "parameters": {
        "color": 7,
        "width": 748,
        "height": 364,
        "content": "## \ud83d\udfe2 Routine \u2014 Low/Medium Risk\nPatients within safe thresholds get a brief status email and their record appended to the daily log. No clinical action required \u2014 this path keeps the audit trail complete."
      },
      "typeVersion": 1
    },
    {
      "id": "cad0203f-ccdd-4cbe-9530-3875419bfe88",
      "name": "Security Notes",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3120,
        1024
      ],
      "parameters": {
        "color": 3,
        "width": 288,
        "height": 226,
        "content": "## \ud83d\udd10 Credentials & Security\nUse OAuth2 for Google Sheets and Gmail. Create a dedicated Telegram bot via @BotFather. Replace all personal emails, sheet IDs, and chat IDs with placeholders before sharing this template."
      },
      "typeVersion": 1
    },
    {
      "id": "6811a77b-bf6d-4891-9322-c9808de92d80",
      "name": "Daily Schedule Trigger (Every 24h)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        176,
        720
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 24
            }
          ]
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "4dafe826-f2a2-423b-b04d-8d509db562f5",
      "name": "Read: Blood Pressure Readings",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        608,
        400
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 462669290,
          "cachedResultName": "BloodPressure"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultName": "chronic_care_google_sheet"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "0ff63aa5-2a6e-4ccc-a702-d99d49eedda9",
      "name": "Read: Glucose Readings",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        608,
        640
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 155610296,
          "cachedResultName": "Glucose"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultName": "chronic_care_google_sheet"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "9300ae3f-051e-4db8-94b8-21d9e4ff7927",
      "name": "Read: Activity & Sleep Data",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        608,
        864
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1087489261,
          "cachedResultName": "Activity"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultName": "chronic_care_google_sheet"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "f7a8dd09-adb9-45aa-b7e9-2e67e578d53d",
      "name": "Merge All Vitals Streams",
      "type": "n8n-nodes-base.merge",
      "position": [
        976,
        704
      ],
      "parameters": {
        "numberInputs": 4
      },
      "typeVersion": 3.2
    },
    {
      "id": "c3ab8f28-165f-4a5c-8726-b5304670663f",
      "name": "Merge: One Structured Record Per Patient",
      "type": "n8n-nodes-base.code",
      "position": [
        1200,
        736
      ],
      "parameters": {
        "jsCode": "// ================================================\n// MERGE ALL SHEETS \u2192 ONE ROW PER PATIENT PER DAY\n// ================================================\n\nconst bpRows       = $('Read: Blood Pressure Readings').all().map(i => i.json);\nconst glucoseRows  = $('Read: Glucose Readings').all().map(i => i.json);\nconst activityRows = $('Read: Activity & Sleep Data').all().map(i => i.json);\nconst patientRows  = $('Read: Patient Master List').all().map(i => i.json);\n\n// Index patients by ID\nconst patientMap = {};\nfor (const p of patientRows) {\n  patientMap[p.PatientID] = p;\n}\n\n// Collect all unique patient IDs seen today\nconst allIDs = new Set([\n  ...bpRows.map(r => r.PatientID),\n  ...glucoseRows.map(r => r.PatientID),\n  ...activityRows.map(r => r.PatientID)\n]);\n\nconst results = [];\n\nfor (const pid of allIDs) {\n  const patient = patientMap[pid] || {};\n\n  const bpList = bpRows.filter(r => r.PatientID === pid);\n  const bp     = bpList.length > 0 ? bpList[bpList.length - 1] : {};\n\n  const glList = glucoseRows.filter(r => r.PatientID === pid);\n  const gl     = glList.length > 0 ? glList[glList.length - 1] : {};\n\n  const acList = activityRows.filter(r => r.PatientID === pid);\n  const ac     = acList.length > 0 ? acList[acList.length - 1] : {};\n\n  results.push({\n    json: {\n      patientId:   pid,\n      patientName: patient.PatientName || bp.PatientName || gl.PatientName || 'Unknown',\n      patientEmail: patient.PatientEmail || '',\n      doctorEmail:  patient.DoctorEmail  || 'user@example.com',\n      condition:    patient.Condition    || '',\n      date:         new Date().toISOString().split('T')[0],\n      targets: {\n        systolic:        parseInt(patient.TargetBP_Sys)         || 130,\n        diastolic:       parseInt(patient.TargetBP_Dia)         || 80,\n        glucoseFasting:  parseInt(patient.TargetGlucose_Fasting)|| 126,\n        steps:           parseInt(patient.TargetSteps)          || 7000\n      },\n      vitals: {\n        bloodPressure: {\n          systolic:  bp.Systolic  ? parseInt(bp.Systolic)  : null,\n          diastolic: bp.Diastolic ? parseInt(bp.Diastolic) : null,\n          pulse:     bp.Pulse     ? parseInt(bp.Pulse)     : null,\n          time:      bp.Time || null\n        },\n        glucose: {\n          level:    gl.GlucoseLevel ? parseFloat(gl.GlucoseLevel) : null,\n          mealType: gl.MealType     || 'unknown',\n          insulin:  gl.InsulinTaken || 'No'\n        }\n      },\n      activity: {\n        steps:         ac.Steps          ? parseInt(ac.Steps)          : null,\n        activeMinutes: ac.ActiveMinutes  ? parseInt(ac.ActiveMinutes)  : null,\n        calories:      ac.CaloriesBurned ? parseInt(ac.CaloriesBurned) : null,\n        weight:        ac.Weight_kg      ? parseFloat(ac.Weight_kg)    : null\n      },\n      sleep: {\n        hours:   ac.SleepHours   ? parseFloat(ac.SleepHours) : null,\n        quality: ac.SleepQuality || null\n      },\n      dataCompleteness: {\n        hasBP:       bp.Systolic     != null,\n        hasGlucose:  gl.GlucoseLevel != null,\n        hasActivity: ac.Steps        != null,\n        hasSleep:    ac.SleepHours   != null\n      }\n    }\n  });\n}\n\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "d4bbbb4e-fa72-41f6-b0a1-565a0074f326",
      "name": "Risk Scoring Engine (AHA/ADA Guidelines)",
      "type": "n8n-nodes-base.code",
      "position": [
        1440,
        736
      ],
      "parameters": {
        "jsCode": "// ================================================\n// AI RISK SCORING ENGINE\n// AHA + ADA clinical guidelines\n// ================================================\n\nconst d = $input.item.json;\nconst { vitals, activity, sleep, targets } = d;\n\nconst anomalies = [];\nlet riskPoints  = 0;\nlet maxRisk     = 0;\n\nif (vitals.bloodPressure.systolic !== null) {\n  const sys = vitals.bloodPressure.systolic;\n  const dia = vitals.bloodPressure.diastolic;\n  maxRisk += 30;\n\n  if (sys >= 180 || dia >= 120) {\n    anomalies.push({ severity:'critical', category:'blood_pressure',\n      message:`Hypertensive Crisis: ${sys}/${dia} mmHg \u2013 IMMEDIATE ACTION` });\n    riskPoints += 30;\n  } else if (sys >= 140 || dia >= 90) {\n    anomalies.push({ severity:'high', category:'blood_pressure',\n      message:`Stage 2 Hypertension: ${sys}/${dia} mmHg` });\n    riskPoints += 20;\n  } else if (sys >= 130 || dia >= 80) {\n    anomalies.push({ severity:'medium', category:'blood_pressure',\n      message:`Stage 1 Hypertension: ${sys}/${dia} mmHg` });\n    riskPoints += 10;\n  } else if (sys < 90 || dia < 60) {\n    anomalies.push({ severity:'high', category:'blood_pressure',\n      message:`Hypotension: ${sys}/${dia} mmHg` });\n    riskPoints += 15;\n  }\n}\n\nif (vitals.glucose.level !== null) {\n  const g  = vitals.glucose.level;\n  const ft = vitals.glucose.mealType.toLowerCase().includes('fast');\n  maxRisk += 30;\n\n  if (g < 70) {\n    anomalies.push({ severity:'critical', category:'glucose',\n      message:`HYPOGLYCEMIA: ${g} mg/dL \u2013 URGENT` });\n    riskPoints += 30;\n  } else if (g >= (ft ? 180 : 250)) {\n    anomalies.push({ severity:'critical', category:'glucose',\n      message:`Severe Hyperglycemia: ${g} mg/dL (${vitals.glucose.mealType})` });\n    riskPoints += 30;\n  } else if (g >= (ft ? 126 : 200)) {\n    anomalies.push({ severity:'high', category:'glucose',\n      message:`Hyperglycemia: ${g} mg/dL (${vitals.glucose.mealType})` });\n    riskPoints += 20;\n  } else if (g >= (ft ? 100 : 140)) {\n    anomalies.push({ severity:'medium', category:'glucose',\n      message:`Pre-diabetic range: ${g} mg/dL (${vitals.glucose.mealType})` });\n    riskPoints += 10;\n  }\n}\n\nif (activity.steps !== null) {\n  maxRisk += 10;\n  if (activity.steps < 2000) {\n    anomalies.push({ severity:'medium', category:'activity',\n      message:`Very low activity: ${activity.steps} steps (target: ${targets.steps})` });\n    riskPoints += 7;\n  } else if (activity.steps < 4000) {\n    anomalies.push({ severity:'low', category:'activity',\n      message:`Below activity target: ${activity.steps} steps` });\n    riskPoints += 3;\n  }\n}\n\nif (sleep.hours !== null) {\n  maxRisk += 10;\n  if (sleep.hours < 5) {\n    anomalies.push({ severity:'medium', category:'sleep',\n      message:`Insufficient sleep: ${sleep.hours}h (target: 7-9h)` });\n    riskPoints += 8;\n  } else if (sleep.hours < 6) {\n    anomalies.push({ severity:'low', category:'sleep',\n      message:`Low sleep: ${sleep.hours}h` });\n    riskPoints += 4;\n  }\n  if (sleep.quality === 'Very Poor') {\n    anomalies.push({ severity:'medium', category:'sleep',\n      message:`Very poor sleep quality reported` });\n    riskPoints += 4;\n  }\n}\n\nconst riskScore = maxRisk > 0 ? Math.min(100, Math.round((riskPoints / maxRisk) * 100)) : 0;\nconst hasCritical = anomalies.some(a => a.severity === 'critical');\nconst hasHigh     = anomalies.some(a => a.severity === 'high');\n\nlet riskLevel, escalate;\nif (hasCritical || riskScore >= 70) { riskLevel = 'CRITICAL'; escalate = true; }\nelse if (hasHigh || riskScore >= 40) { riskLevel = 'HIGH';     escalate = true; }\nelse if (riskScore >= 20)            { riskLevel = 'MEDIUM';   escalate = false; }\nelse                                  { riskLevel = 'LOW';      escalate = false; }\n\nreturn [{ json: {\n  ...d,\n  analysis: {\n    riskScore, riskLevel, escalate,\n    anomalies,\n    totalAnomalies: anomalies.length,\n    criticalCount:  anomalies.filter(a => a.severity === 'critical').length,\n    timestamp: new Date().toISOString()\n  }\n}}];"
      },
      "typeVersion": 2
    },
    {
      "id": "c08cb301-7be5-4dde-8597-57dac4eaa952",
      "name": "Route: Escalate to Clinician?",
      "type": "n8n-nodes-base.if",
      "position": [
        1664,
        736
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-1",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.analysis.escalate }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "05523450-d6b1-4437-9e2b-c2c2df2ad4aa",
      "name": "Generate Clinical Intervention Plan",
      "type": "n8n-nodes-base.code",
      "position": [
        1968,
        592
      ],
      "parameters": {
        "jsCode": "// ================================================\n// CLINICAL INTERVENTION RECOMMENDATIONS\n// ================================================\n\nconst d = $input.item.json;\nconst { analysis, vitals, activity, sleep } = d;\nconst recs = [];\n\nconst bp  = vitals.bloodPressure;\nconst glu = vitals.glucose;\n\nif (bp.systolic >= 180) {\n  recs.push({ priority:1, action:'IMMEDIATE',\n    rec:'Emergency evaluation. Hypertensive crisis protocol. Check for headache, chest pain, or vision changes. Consider IV antihypertensive.',\n    followUp:'2 hours' });\n} else if (bp.systolic >= 140) {\n  recs.push({ priority:2, action:'REVIEW MEDICATION',\n    rec:'Review antihypertensive adherence and dosing. Consider adjusting ACE inhibitor/ARB. Home BP monitoring twice daily.',\n    followUp:'24 hours' });\n  recs.push({ priority:4, action:'LIFESTYLE',\n    rec:'DASH diet counselling. Sodium restriction <2,300mg/day. 30 min aerobic activity 5x/week.',\n    followUp:'1 week' });\n} else if (bp.systolic >= 130) {\n  recs.push({ priority:3, action:'MONITOR',\n    rec:'Stage 1 hypertension. Reinforce lifestyle changes. Reassess in 1 month.',\n    followUp:'1 month' });\n}\n\nif (bp.systolic !== null && bp.systolic < 90) {\n  recs.push({ priority:1, action:'IMMEDIATE',\n    rec:'Hypotension detected. Check for dehydration, medication side effects, or orthostatic hypotension. Increase fluid intake.',\n    followUp:'4 hours' });\n}\n\nif (glu.level !== null) {\n  const g = glu.level;\n  if (g < 70) {\n    recs.push({ priority:1, action:'IMMEDIATE',\n      rec:'Hypoglycaemia protocol: 15-20g fast-acting carbs now (juice/glucose tablets). Recheck in 15 min. Review insulin dosing.',\n      followUp:'15 minutes' });\n  } else if (g > 250) {\n    recs.push({ priority:2, action:'CONTACT PATIENT',\n      rec:'Severe hyperglycaemia. Check ketones if T1D. Review medication adherence. Assess for illness/stress trigger.',\n      followUp:'4 hours' });\n  } else if (g > 180) {\n    recs.push({ priority:3, action:'REVIEW MEDICATION',\n      rec:'Uncontrolled hyperglycaemia. Review current diabetes medication. Consider dose adjustment or adding GLP-1 agonist.',\n      followUp:'48 hours' });\n  }\n}\n\nif (activity.steps !== null && activity.steps < 2000) {\n  recs.push({ priority:4, action:'LIFESTYLE',\n    rec:'Severe sedentary behaviour. Prescribe structured 20-min daily walks. Consider physiotherapy referral.',\n    followUp:'1 week' });\n}\n\nif (sleep.hours !== null && sleep.hours < 5) {\n  recs.push({ priority:4, action:'LIFESTYLE',\n    rec:'Severe sleep deprivation worsens BP and glucose control. Screen for sleep apnoea. CBT-I or sleep hygiene programme.',\n    followUp:'2 weeks' });\n}\n\nrecs.sort((a,b) => a.priority - b.priority);\n\nreturn [{ json: {\n  ...d,\n  interventions: {\n    recommendations: recs,\n    totalRecs: recs.length,\n    immediateActions: recs.filter(r => r.action === 'IMMEDIATE').length,\n    generatedAt: new Date().toISOString()\n  }\n}}];"
      },
      "typeVersion": 2
    },
    {
      "id": "4c3ae566-bd08-4628-be98-d38338727037",
      "name": "Telegram: Send Risk Alert to Doctor",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2288,
        432
      ],
      "parameters": {
        "text": "=\ud83d\udea8 *{{ $json.analysis.riskLevel }} RISK \u2013 {{ $json.patientName }}*\n\n\ud83d\udc64 *Patient:* {{ $json.patientName }} ({{ $json.patientId }})\n\ud83c\udfe5 *Condition:* {{ $json.condition }}\n\ud83d\udcc5 *Date:* {{ $json.date }}\n\u26a0\ufe0f *Risk Score:* {{ $json.analysis.riskScore }}/100\n\n\ud83d\udcca *VITALS*\n{{ $json.vitals.bloodPressure.systolic !== null ? '\ud83e\ude7a BP: ' + $json.vitals.bloodPressure.systolic + '/' + $json.vitals.bloodPressure.diastolic + ' mmHg\\n' : '\ud83e\ude7a BP: not logged\\n' }}{{ $json.vitals.glucose.level !== null ? '\ud83e\ude78 Glucose: ' + $json.vitals.glucose.level + ' mg/dL (' + $json.vitals.glucose.mealType + ')\\n' : '\ud83e\ude78 Glucose: not logged\\n' }}\ud83d\udeb6 Steps: {{ $json.activity.steps ?? 'not logged' }}\n\ud83d\ude34 Sleep: {{ $json.sleep.hours ?? 'not logged' }}h ({{ $json.sleep.quality ?? '-' }})\n\n\ud83d\udd34 *ANOMALIES ({{ $json.analysis.totalAnomalies }})*\n{{ $json.analysis.anomalies.map(a => (a.severity==='critical'?'\ud83d\udd34':a.severity==='high'?'\ud83d\udfe0':'\ud83d\udfe1') + ' ' + a.message).join('\\n') }}\n\n_Chronic Care Monitoring Agent_",
        "chatId": "YOUR_DOCTOR_TELEGRAM_CHAT_ID",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "27d9c611-1813-4d02-be15-dca8cf158672",
      "name": "Gmail: Send Escalation Alert Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2288,
        720
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=<h2>\ud83d\udea8 Patient Risk Alert</h2><p><strong>Patient:</strong> {{ $json.patientName }} ({{ $json.patientId }})</p><p><strong>Condition:</strong> {{ $json.condition }}</p><p><strong>Date:</strong> {{ $json.date }}</p><hr><h3>Risk Assessment</h3><p><strong>Risk Level:</strong> {{ $json.analysis.riskLevel }}<br><strong>Risk Score:</strong> {{ $json.analysis.riskScore }}/100<br><strong>Total Anomalies:</strong> {{ $json.analysis.totalAnomalies }}</p><hr><h3>Vitals</h3><ul><li>Blood Pressure: {{ $json.vitals.bloodPressure.systolic }}/{{ $json.vitals.bloodPressure.diastolic }} mmHg</li><li>Pulse: {{ $json.vitals.bloodPressure.pulse }} bpm</li><li>Glucose: {{ $json.vitals.glucose.level }} mg/dL ({{ $json.vitals.glucose.mealType }})</li><li>Steps: {{ $json.activity.steps }}</li><li>Sleep: {{ $json.sleep.hours }} hrs</li></ul><hr><h3>Detected Issues</h3><ul>{{ $json.analysis.anomalies.map(a => `<li><b>${a.severity.toUpperCase()}</b> - ${a.message}</li>`).join('') }}</ul><hr><h3>Recommended Action</h3><p>Please review this patient immediately and determine whether intervention, medication adjustment, or patient outreach is required.</p><p>Generated by Chronic Care Monitoring AI Workflow</p>",
        "options": {},
        "subject": "=\ud83d\udea8 {{ $json.analysis.riskLevel }} Risk Alert - {{ $json.patientName }} ({{ $json.patientId }})"
      },
      "typeVersion": 2.2
    },
    {
      "id": "ce9b25cd-0b8f-4049-a3dd-3f5560b809a8",
      "name": "Merge: Telegram + Email Outputs",
      "type": "n8n-nodes-base.merge",
      "position": [
        2592,
        560
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "a0779d72-77a3-451d-ae8f-96a0b0b58db5",
      "name": "Log: Write Escalated Record to Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2880,
        560
      ],
      "parameters": {
        "columns": {
          "value": {
            "Date": "={{ $('Risk Scoring Engine (AHA/ADA Guidelines)').item.json.date }}",
            "Steps": "={{ $('Risk Scoring Engine (AHA/ADA Guidelines)').item.json.activity.steps }}",
            "Systolic": "={{ $('Risk Scoring Engine (AHA/ADA Guidelines)').item.json.vitals.bloodPressure.systolic }}",
            "Anomalies": "={{ $('Risk Scoring Engine (AHA/ADA Guidelines)').item.json.analysis.anomalies }}",
            "Diastolic": "={{ $('Risk Scoring Engine (AHA/ADA Guidelines)').item.json.vitals.bloodPressure.diastolic }}",
            "Escalated": "={{ $('Risk Scoring Engine (AHA/ADA Guidelines)').item.json.analysis.escalate }}",
            "PatientID": "={{ $('Risk Scoring Engine (AHA/ADA Guidelines)').item.json.patientId }}",
            "RiskLevel": "={{ $('Risk Scoring Engine (AHA/ADA Guidelines)').item.json.analysis.riskLevel }}",
            "RiskScore": "={{ $('Risk Scoring Engine (AHA/ADA Guidelines)').item.json.analysis.riskScore }}",
            "Timestamp": "={{ $('Risk Scoring Engine (AHA/ADA Guidelines)').item.json.analysis.timestamp }}",
            "SleepHours": "={{ $('Risk Scoring Engine (AHA/ADA Guidelines)').item.json.sleep.hours }}",
            "PatientName": "={{ $('Risk Scoring Engine (AHA/ADA Guidelines)').item.json.patientName }}",
            "AnomalyCount": "={{ $('Risk Scoring Engine (AHA/ADA Guidelines)').item.json.analysis.totalAnomalies }}"
          },
          "schema": [
            {
              "id": "Date",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "PatientID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "PatientID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "PatientName",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "PatientName",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "RiskScore",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "RiskScore",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "RiskLevel",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "RiskLevel",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Escalated",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Escalated",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Systolic",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Systolic",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Diastolic",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Diastolic",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "HeartRate",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "HeartRate",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Glucose",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Glucose",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "GlucoseMeal",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "GlucoseMeal",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Steps",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Steps",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "SleepHours",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "SleepHours",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "AnomalyCount",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "AnomalyCount",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Anomalies",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Anomalies",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Recommendations",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Recommendations",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Timestamp",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "PatientID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "DailyLog"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultName": "chronic_care_google_sheet"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "49d49c16-47c4-4c3b-8957-4cb32bd225b3",
      "name": "Log: Write Normal Record to Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2064,
        1232
      ],
      "parameters": {
        "columns": {
          "value": {
            "Date": "={{ $json.date }}",
            "Steps": "={{ $json.activity.steps }}",
            "Glucose": "={{ $json.vitals.glucose.level }}",
            "Systolic": "={{ $json.vitals.bloodPressure.systolic }}",
            "Anomalies": "={{ $json.analysis.anomalies.map(a => a.message).join(' | ') }}",
            "Diastolic": "={{ $json.vitals.bloodPressure.diastolic }}",
            "Escalated": "=false",
            "PatientID": "={{ $json.patientId }}",
            "RiskLevel": "={{ $json.analysis.riskLevel }}",
            "RiskScore": "={{ $json.analysis.riskScore }}",
            "Timestamp": "={{ $json.analysis.timestamp }}",
            "SleepHours": "={{ $json.sleep.hours }}",
            "GlucoseMeal": "={{ $json.vitals.glucose.mealType }}",
            "PatientName": "={{ $json.patientName }}",
            "AnomalyCount": "={{ $json.analysis.totalAnomalies }}"
          },
          "schema": [
            {
              "id": "Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "PatientID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "PatientID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "PatientName",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "PatientName",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "RiskScore",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "RiskScore",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "RiskLevel",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "RiskLevel",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Escalated",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Escalated",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Systolic",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Systolic",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Diastolic",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Diastolic",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "HeartRate",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "HeartRate",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Glucose",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Glucose",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "GlucoseMeal",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "GlucoseMeal",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Steps",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Steps",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "SleepHours",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "SleepHours",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "AnomalyCount",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "AnomalyCount",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Anomalies",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Anomalies",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Recommendations",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Recommendations",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1523249484,
          "cachedResultName": "DailyLog"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultName": "chronic_care_google_sheet"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "a31e5e9c-ae9d-4c40-8c37-8a0e9643204d",
      "name": "Gmail: Send Routine Status Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2480,
        1232
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=<h2>\ud83d\udfe2 Patient Status Update</h2>\n\n<p><strong>Patient:</strong> {{ $json.patientName }} ({{ $json.patientId }})</p>\n\n<p><strong>Condition:</strong> {{ $json.condition }}</p>\n\n<p><strong>Date:</strong> {{ $json.date }}</p>\n\n<hr>\n\n<h3>Risk Summary</h3>\n\n<p>\n<strong>Risk Level:</strong> {{ $json.analysis.riskLevel }}<br>\n<strong>Risk Score:</strong> {{ $json.analysis.riskScore }}/100<br>\n<strong>Escalation Required:</strong> No\n</p>\n\n<hr>\n\n<h3>Latest Vitals</h3>\n\n<ul>\n<li>Blood Pressure: {{ $json.vitals.bloodPressure.systolic }}/{{ $json.vitals.bloodPressure.diastolic }} mmHg</li>\n<li>Pulse: {{ $json.vitals.bloodPressure.pulse }} bpm</li>\n<li>Glucose: {{ $json.vitals.glucose.level }} mg/dL ({{ $json.vitals.glucose.mealType }})</li>\n<li>Steps: {{ $json.activity.steps }}</li>\n<li>Sleep: {{ $json.sleep.hours }} hrs</li>\n</ul>\n\n<hr>\n\n<h3>Observation</h3>\n\n<p>\nPatient remains within acceptable monitoring thresholds today.\nNo immediate clinical intervention is required.\nContinue routine monitoring and follow the current care plan.\n</p>\n\n<p>Generated by Chronic Care Monitoring AI Workflow</p>",
        "options": {},
        "subject": "=\u2705 Daily Monitoring Update - {{ $json.patientName }} ({{ $json.patientId }})"
      },
      "typeVersion": 2.2
    },
    {
      "id": "0c093c43-1f42-4a74-8e62-72e9386836c4",
      "name": "\ud83d\udc65 Read: Patient Master List",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Reads all patients from Patients tab to get targets and contact info.",
      "position": [
        608,
        1088
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1992383121,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/133Pn_5kWnuAeaqyerfFdH62_eQa6shbZqeXP1qAx_TE/edit#gid=1992383121",
          "cachedResultName": "Patients"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "133Pn_5kWnuAeaqyerfFdH62_eQa6shbZqeXP1qAx_TE",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/133Pn_5kWnuAeaqyerfFdH62_eQa6shbZqeXP1qAx_TE/edit?usp=drivesdk",
          "cachedResultName": "chronic_care_google_sheet"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "75c2f495-eda7-4ff3-b3af-ae0e93c59750",
  "connections": {
    "On Workflow Error": {
      "main": [
        [
          {
            "node": "Slack: Error Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read: Glucose Readings": {
      "main": [
        [
          {
            "node": "Merge All Vitals Streams",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge All Vitals Streams": {
      "main": [
        [
          {
            "node": "Merge: One Structured Record Per Patient",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read: Activity & Sleep Data": {
      "main": [
        [
          {
            "node": "Merge All Vitals Streams",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Read: Blood Pressure Readings": {
      "main": [
        [
          {
            "node": "Merge All Vitals Streams",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route: Escalate to Clinician?": {
      "main": [
        [
          {
            "node": "Generate Clinical Intervention Plan",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log: Write Normal Record to Sheet",
            "type": "main",
            "index": 0
          },
          {
            "node": "Gmail: Send Routine Status Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udc65 Read: Patient Master List": {
      "main": [
        [
          {
            "node": "Merge All Vitals Streams",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "Merge: Telegram + Email Outputs": {
      "main": [
        [
          {
            "node": "Log: Write Escalated Record to Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log: Write Normal Record to Sheet": {
      "main": [
        [
          {
            "node": "Gmail: Send Routine Status Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily Schedule Trigger (Every 24h)": {
      "main": [
        [
          {
            "node": "Read: Blood Pressure Readings",
            "type": "main",
            "index": 0
          },
          {
            "node": "Read: Glucose Readings",
            "type": "main",
            "index": 0
          },
          {
            "node": "Read: Activity & Sleep Data",
            "type": "main",
            "index": 0
          },
          {
            "node": "\ud83d\udc65 Read: Patient Master List",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gmail: Send Escalation Alert Email": {
      "main": [
        [
          {
            "node": "Merge: Telegram + Email Outputs",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Generate Clinical Intervention Plan": {
      "main": [
        [
          {
            "node": "Telegram: Send Risk Alert to Doctor",
            "type": "main",
            "index": 0
          },
          {
            "node": "Gmail: Send Escalation Alert Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram: Send Risk Alert to Doctor": {
      "main": [
        [
          {
            "node": "Merge: Telegram + Email Outputs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge: One Structured Record Per Patient": {
      "main": [
        [
          {
            "node": "Risk Scoring Engine (AHA/ADA Guidelines)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Risk Scoring Engine (AHA/ADA Guidelines)": {
      "main": [
        [
          {
            "node": "Route: Escalate to Clinician?",
            "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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

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 when escalation is needed, while logging all results back to Google Sheets. Runs…

Source: https://n8n.io/workflows/16604/ — original creator credit. Request a take-down →

More Email & Gmail workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Email & Gmail

This workflow automatically detects bounced or invalid email addresses from your Gmail inbox and updates their status in Google Sheets. It fetches bounce notifications, extracts failed email addresses

Google Sheets, Slack, Gmail
Email & Gmail

This workflow automatically handles every resolved Jira bug by verifying the fix, notifying the customer, updating HubSpot, commenting on the Jira issue, alerting the team on Slack, and logging everyt

Jira Trigger, Jira, Gmail +4
Email & Gmail

This workflow triggers when a new deal is created in HubSpot, sends a two-step follow-up sequence via Gmail, checks HubSpot for a recent email reply, then updates the deal stage, alerts a Slack channe

HubSpot Trigger, HubSpot, Google Sheets +2
Email & Gmail

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.

High Level, Gmail, Google Sheets +2
Email & Gmail

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 sch

Google Sheets, Slack, Gmail +1