AutomationFlowsSlack & Telegram › Check Prescription Safety and Drug Interactions with Gpt-4o, Sheets, and…

Check Prescription Safety and Drug Interactions with Gpt-4o, Sheets, and…

Original n8n title: Check Prescription Safety and Drug Interactions with Gpt-4o, Sheets, and Telegram

ByRahul Joshi @rahul08 on n8n.io

This workflow monitors a Google Drive folder for new prescription PDFs, extracts the text, uses OpenAI (GPT-4o-mini) to structure prescription details and draft a SOAP note, checks drugs against Google Sheets formulary and interaction tables, and notifies a pharmacist or patient…

Event trigger★★★★☆ complexityAI-powered24 nodesOpenAITelegramGoogle Drive TriggerGoogle DriveGoogle SheetsError TriggerSlack
Slack & Telegram Trigger: Event Nodes: 24 Complexity: ★★★★☆ AI nodes: yes Added:
Check Prescription Safety and Drug Interactions with Gpt-4o, Sheets, and… — n8n workflow card showing OpenAI, Telegram, Google Drive Trigger integration

This workflow corresponds to n8n.io template #16458 — we link there as the canonical source.

This workflow follows the Error Trigger → 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 →

Download .json
{
  "id": "xK0V9TvJK4zngSNl",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "AI-Powered Prescription Safety & Drug Interaction Checker",
  "tags": [],
  "nodes": [
    {
      "id": "e075392c-0b23-46ce-baf4-d934452a2c7b",
      "name": "Overview: Prescription Safety Checker",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3888,
        2224
      ],
      "parameters": {
        "width": 700,
        "height": 548,
        "content": "## \ud83d\udc8a AI-Powered Prescription Safety Checker\n\n### How it works\nWhen a prescription PDF is uploaded to a designated Google Drive folder, this workflow automatically extracts the text, uses GPT-4o to parse patient and drug details and draft a clinical SOAP note, then cross-references every drug against your formulary and known interaction tables. If a problem is found, the pharmacist gets an immediate Telegram alert and the case is logged for review. Clean prescriptions trigger a patient confirmation message and are filed in the Prescriptions sheet.\n\n### Setup steps\n1. **Google Drive** \u2014 Connect your OAuth2 account and set the folder ID where prescription PDFs will be uploaded.\n2. **OpenAI** \u2014 Add your OpenAI API key credential (GPT-4o-mini is used by default; upgrade if needed).\n3. **Google Sheets** \u2014 Connect your Sheets OAuth2 account. Create a spreadsheet with three tabs: `Formulary`, `Interactions`, and `Prescriptions`, plus a `FlaggedPrescriptions` tab. Update the sheet IDs in the relevant nodes.\n4. **Telegram** \u2014 Connect your Telegram Bot credential and replace the `chatId` values with the pharmacist's and patient's chat IDs.\n5. **Slack (Error Handler)** \u2014 Connect your Slack OAuth2 account and set the correct channel for workflow error alerts.\n6. **Test** \u2014 Upload a sample prescription PDF to the Drive folder and confirm the full flow runs end-to-end."
      },
      "typeVersion": 1
    },
    {
      "id": "9a50cb98-336c-49d6-bc8e-d4c86d2aaf73",
      "name": "Section: Trigger & PDF Extraction",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3120,
        2832
      ],
      "parameters": {
        "color": 7,
        "width": 508,
        "height": 540,
        "content": "## \ud83d\udce5 Trigger & PDF Extraction\nWatches a Google Drive folder for new prescription PDFs, downloads the file, and pulls the raw text out \u2014 ready to hand off to the AI step. Change the folder ID to your own prescriptions intake folder."
      },
      "typeVersion": 1
    },
    {
      "id": "cc761157-76ec-46de-9595-bd1b5285f437",
      "name": "Section: AI Extraction & SOAP Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2592,
        2816
      ],
      "parameters": {
        "color": 7,
        "width": 540,
        "height": 556,
        "content": "## \ud83e\udd16 AI Extraction & SOAP Note\nSends raw PDF text to GPT-4o in a single call: it extracts structured prescription fields (patient, doctor, drugs, dosage, route) and drafts a clinical SOAP note simultaneously. The Code node then parses and validates the JSON before passing it downstream."
      },
      "typeVersion": 1
    },
    {
      "id": "2da8b9b0-affa-4966-993b-ad0be8e58914",
      "name": "Section: Formulary & Interaction Checks",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2016,
        2736
      ],
      "parameters": {
        "color": 7,
        "width": 1276,
        "height": 668,
        "content": "## \ud83d\udccb Formulary & Interaction Checks\nReads your approved drug formulary and known drug-pair interaction table from Google Sheets, then compares every prescribed drug against both lists. Flags any off-formulary drugs and any dangerous combinations before routing the prescription."
      },
      "typeVersion": 1
    },
    {
      "id": "afc45d93-d3e4-4dd9-b4a0-484177e757b6",
      "name": "Section: Alert & Logging (Flagged)",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -656,
        2608
      ],
      "parameters": {
        "color": 7,
        "width": 572,
        "height": 396,
        "content": "## \ud83d\udea8 Alert & Logging \u2014 Flagged Prescriptions\nIf interactions or formulary issues are detected, a Telegram message is sent to the pharmacist with full interaction details and a SOAP note preview. The case is then appended to the FlaggedPrescriptions audit sheet for traceability."
      },
      "typeVersion": 1
    },
    {
      "id": "c034195f-9d2d-42a1-a2f1-c83010c24679",
      "name": "Section: Confirmation & Logging (Cleared)",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -656,
        3056
      ],
      "parameters": {
        "color": 7,
        "width": 588,
        "height": 444,
        "content": "## \u2705 Confirmation & Logging \u2014 Cleared Prescriptions\nFor prescriptions with no issues, a patient-facing Telegram confirmation is sent and the prescription record is appended to the Prescriptions master sheet. This closes the loop cleanly without requiring any manual pharmacist action."
      },
      "typeVersion": 1
    },
    {
      "id": "206404a4-e640-4520-a619-623dea2f3d2d",
      "name": "Credentials & Security",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        80,
        3408
      ],
      "parameters": {
        "color": 3,
        "width": 348,
        "height": 212,
        "content": "## \ud83d\udd10 Credentials & Security\nUse OAuth2 for Google Drive and Google Sheets. Use API key credentials for OpenAI and Bot Token credentials for Telegram. Replace all `chatId` values and sheet/folder IDs with your own \u2014 never share personal identifiers in published templates."
      },
      "typeVersion": 1
    },
    {
      "id": "f6fa084e-2709-418f-87a7-98efe2eafefa",
      "name": "GPT-4o \u2013 Extract + Draft SOAP Note",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "notes": "Receives raw PDF text from Extract from File. Extracts structured prescription data AND generates SOAP note in one GPT call. Returns a single JSON object.",
      "onError": "continueErrorOutput",
      "position": [
        -2336,
        3088
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "GPT-4O-MINI"
        },
        "options": {
          "maxTokens": 1500,
          "temperature": 0.2
        },
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "You are a clinical pharmacist assistant. You will receive raw text extracted from a prescription PDF. Your job is to do TWO things in one response:\n\n1. Extract structured prescription data.\n2. Write a clinical SOAP note based on that data.\n\nReturn ONLY a valid JSON object \u2014 no markdown, no backticks, no explanation. Use this exact shape:\n{\n  \"patient_name\": \"...\",\n  \"patient_id\": \"...\",\n  \"date\": \"...\",\n  \"prescribing_doctor\": \"...\",\n  \"diagnosis\": \"...\",\n  \"notes\": \"...\",\n  \"drugs\": [\n    {\n      \"name\": \"...\",\n      \"generic_name\": \"...\",\n      \"dosage\": \"...\",\n      \"frequency\": \"...\",\n      \"duration\": \"...\",\n      \"route\": \"...\"\n    }\n  ],\n  \"soap_note\": \"S: ...\\nO: ...\\nA: ...\\nP: ...\"\n}\n\nIf any field is not found in the text, use null. Always return valid JSON only."
            },
            {
              "content": "={{ $json.text }}"
            }
          ]
        }
      },
      "typeVersion": 1.4
    },
    {
      "id": "4b6ae6f2-e36b-442f-a3cf-64c5d78dc02d",
      "name": "Parse & Validate GPT Response",
      "type": "n8n-nodes-base.code",
      "notes": "Parses GPT-4o JSON response. Extracts prescription fields + soap_note + drug list.",
      "position": [
        -1920,
        3072
      ],
      "parameters": {
        "jsCode": "// Parse Extracted Data2\n// Input: GPT-4o response \u2014 with simplifyOutput ON, the text is at $json.message.content\n// Falls back to choices[0].message.content for safety.\n\nconst gptOutput = $input.item.json;\n\nconst rawContent =\n  gptOutput?.message?.content ||\n  gptOutput?.choices?.[0]?.message?.content ||\n  gptOutput?.text ||\n  '';\n\nif (!rawContent) {\n  throw new Error(\n    'GPT-4o returned empty content. Keys found: ' + Object.keys(gptOutput).join(', ')\n  );\n}\n\nlet parsed;\ntry {\n  parsed = JSON.parse(rawContent);\n} catch (e) {\n  const clean = rawContent.replace(/```json|```/g, '').trim();\n  try {\n    parsed = JSON.parse(clean);\n  } catch (e2) {\n    throw new Error('Failed to parse GPT JSON response: ' + rawContent.substring(0, 400));\n  }\n}\n\nif (!parsed.drugs || !Array.isArray(parsed.drugs)) {\n  throw new Error('GPT did not return a valid drugs array. Response: ' + JSON.stringify(parsed).substring(0, 300));\n}\n\nconst drugNames = parsed.drugs\n  .map(d => (d.generic_name || d.name || '').toLowerCase().trim())\n  .filter(Boolean);\n\nreturn {\n  ...parsed,\n  drug_names_flat: drugNames.join(','),\n  drug_list: drugNames,\n  source_file_id: $('Download PDF from Drive').item.json.id,\n  source_file_name: $('Watch Prescriptions Folder').item.json.name,\n  extracted_at: new Date().toISOString()\n};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "e74661be-7427-4189-bdf0-5325fdd135d0",
      "name": "Route: Flagged or Cleared?",
      "type": "n8n-nodes-base.if",
      "notes": "TRUE \u2192 alert pharmacist branch | FALSE \u2192 clean prescription branch",
      "position": [
        -896,
        3072
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "condition_needs_alert",
              "operator": {
                "type": "boolean",
                "operation": "equal"
              },
              "leftValue": "={{ $json.needs_pharmacist_alert }}",
              "rightValue": true
            },
            {
              "id": "4714ef5e-56d6-44f0-aa70-567e362c1479",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "d42362e4-f71e-4562-9494-1a16738cca95",
      "name": "Telegram \u2013 Alert Pharmacist",
      "type": "n8n-nodes-base.telegram",
      "notes": "Sends flagged interaction details + SOAP preview to the pharmacist",
      "position": [
        -560,
        2800
      ],
      "parameters": {
        "text": "=\ud83d\udea8 *PRESCRIPTION ALERT \u2014 ACTION REQUIRED*\n\n\ud83d\udccb *Patient:* {{ $json.patient_name || 'Unknown' }}\n\ud83c\udd94 *Patient ID:* {{ $json.patient_id || 'N/A' }}\n\ud83d\udc68\u200d\u2695\ufe0f *Doctor:* {{ $json.prescribing_doctor || 'Unknown' }}\n\ud83d\udcc5 *Date:* {{ $json.date || 'N/A' }}\n\ud83d\udcc1 *File:* {{ $json.source_file_name }}\n\n\ud83d\udc8a *Prescribed Drugs:*\n{{ $json.drugs.map(d => `\u2022 ${d.name} ${d.dosage} \u2014 ${d.frequency}`).join('\\n') }}\n\n{{ $json.has_interactions ? '\u26a0\ufe0f *DRUG INTERACTIONS DETECTED:*\\n' + $json.interactions.map(i => `\u274c ${i.drugA} \u2194 ${i.drugB}\\n   Severity: ${i.severity}\\n   ${i.description}\\n   Action: ${i.action}`).join('\\n\\n') : '' }}\n\n{{ $json.has_formulary_issues ? '\ud83d\udeab *NOT IN FORMULARY:*\\n' + $json.not_in_formulary.map(d => `\u2022 ${d}`).join('\\n') : '' }}\n\n\ud83d\udcdd *SOAP Note Preview:*\n{{ ($json.soap_note || '').substring(0, 600) }}\u2026\n\n\u23f0 Flagged at: {{ $json.check_timestamp }}\n_Please review before dispensing._",
        "chatId": "=YOUR_PHARMACIST_CHAT_ID",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "39bc9adb-49e9-45d7-880f-67b0e65926d3",
      "name": "Telegram \u2013 Notify Patient",
      "type": "n8n-nodes-base.telegram",
      "notes": "Sends patient/clinic confirmation that prescription is clear and ready",
      "position": [
        -208,
        3296
      ],
      "parameters": {
        "text": "=\u2705 *Prescription Received & Cleared*\n\n\ud83d\udc64 *Patient:* {{ $json.patient_name || 'Patient' }}\n\nYour prescription has been reviewed and is ready for dispensing.\n\n\ud83d\udc8a *Medications:*\n{{ $json.drugs.map(d => `\u2022 ${d.name} ${d.dosage} \u2014 ${d.frequency}${d.duration ? ' for ' + d.duration : ''}`).join('\\n') }}\n\n\ud83d\udcc5 *Prescription Date:* {{ $json.date }}\n\ud83d\udc68\u200d\u2695\ufe0f *Prescribed by:* {{ $json.prescribing_doctor }}\n\nPlease collect your medications from the pharmacy.\n_Questions? Contact your pharmacist._",
        "chatId": "=YOUR_PATIENT_CHAT_ID",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "5cb83aeb-3696-4f32-9634-203b7c1941ae",
      "name": "Watch Prescriptions Folder",
      "type": "n8n-nodes-base.googleDriveTrigger",
      "notes": "Watches for new prescription PDFs uploaded to the designated Drive folder",
      "position": [
        -3056,
        3088
      ],
      "parameters": {
        "event": "fileCreated",
        "options": {},
        "pollTimes": {
          "item": [
            {
              "mode": "everyMinute"
            }
          ]
        },
        "triggerOn": "specificFolder",
        "folderToWatch": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_DRIVE_FOLDER_ID",
          "cachedResultUrl": "https://drive.google.com/drive/folders/YOUR_GOOGLE_DRIVE_FOLDER_ID",
          "cachedResultName": "Prescriptions Intake Folder"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "5a91baf3-357b-4870-babd-a41c9628a0b7",
      "name": "Download PDF from Drive",
      "type": "n8n-nodes-base.googleDrive",
      "notes": "Downloads the uploaded prescription PDF as binary data",
      "position": [
        -2784,
        3088
      ],
      "parameters": {
        "fileId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.id }}"
        },
        "options": {},
        "operation": "download"
      },
      "typeVersion": 3
    },
    {
      "id": "83257431-57db-457e-b45b-82121403914b",
      "name": "Read Approved Drug Formulary",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Reads the master drug formulary list from Google Sheets",
      "position": [
        -1648,
        2896
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "Formulary",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit#gid=Formulary",
          "cachedResultName": "Formulary"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit?usp=drivesdk",
          "cachedResultName": "Pharmacy Database"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "d905ce0e-d1bb-43da-8a2b-7370f8cb091a",
      "name": "Read Drug Interactions Table",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Reads the known drug interaction pairs table from Google Sheets",
      "position": [
        -1648,
        3232
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "Interactions",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit#gid=Interactions",
          "cachedResultName": "Interactions"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit?usp=drivesdk",
          "cachedResultName": "Pharmacy Database"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "62b28feb-ef32-4d98-8943-53e8304abc70",
      "name": "Check Formulary & Flag Interactions",
      "type": "n8n-nodes-base.code",
      "notes": "Cross-references extracted drugs against formulary and interaction tables",
      "position": [
        -1168,
        3072
      ],
      "parameters": {
        "jsCode": "const prescription = $('Parse & Validate GPT Response').item.json;\nconst drugList = prescription.drug_list || [];\n\nconst formularyRows   = $('Read Approved Drug Formulary').all().map(i => i.json);\nconst interactionRows = $('Read Drug Interactions Table').all().map(i => i.json);\n\n// Formulary check\nconst formularyDrugs = formularyRows.map(r =>\n  (r['drug_name'] || r['Drug Name'] || '').toLowerCase().trim()\n);\nconst notInFormulary = drugList.filter(d =>\n  !formularyDrugs.some(f => f.includes(d) || d.includes(f))\n);\n\n// Interaction check\nconst interactions = [];\nfor (const row of interactionRows) {\n  const drugA       = (row['drug_a']         || row['Drug A']       || '').toLowerCase().trim();\n  const drugB       = (row['drug_b']         || row['Drug B']       || '').toLowerCase().trim();\n  const severity    =  row['severity']       || row['Severity']      || 'Unknown';\n  const description =  row['description']    || row['Description']   || '';\n  const action      =  row['clinical_action'] || '';\n\n  const aPresent = drugList.some(d => d.includes(drugA) || drugA.includes(d));\n  const bPresent = drugList.some(d => d.includes(drugB) || drugB.includes(d));\n\n  if (aPresent && bPresent) {\n    interactions.push({ drugA, drugB, severity, description, action });\n  }\n}\n\nconst hasInteractions    = interactions.length > 0;\nconst hasFormularyIssues = notInFormulary.length > 0;\nconst needsAlert         = hasInteractions || hasFormularyIssues;\n\nreturn {\n  ...prescription,\n  interactions,\n  not_in_formulary: notInFormulary,\n  has_interactions: hasInteractions,\n  has_formulary_issues: hasFormularyIssues,\n  needs_pharmacist_alert: needsAlert,\n  check_timestamp: new Date().toISOString()\n};\n"
      },
      "typeVersion": 2
    },
    {
      "id": "f1e49fdc-842f-4dba-b508-a9aae1f3ec91",
      "name": "Log Flagged Prescription to Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Appends flagged prescription + SOAP note to the FlaggedPrescriptions audit sheet",
      "position": [
        -288,
        2800
      ],
      "parameters": {
        "columns": {
          "value": {
            "drugs": "={{ $json.drug_names_flat }}",
            "doctor": "={{ $json.prescribing_doctor }}",
            "status": "FLAGGED \u2014 Awaiting Pharmacist Review",
            "timestamp": "={{ $json.check_timestamp }}",
            "patient_id": "={{ $json.patient_id }}",
            "source_file": "={{ $json.source_file_name }}",
            "patient_name": "={{ $json.patient_name }}",
            "formulary_issues": "={{ $json.not_in_formulary.join('; ') }}",
            "interactions_found": "={{ $json.interactions.map(i => i.drugA + ' \u2194 ' + i.drugB + ' [' + i.severity + ']').join('; ') }}"
          },
          "schema": [
            {
              "id": "timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "patient_name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "patient_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "patient_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "patient_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "doctor",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "doctor",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "drugs",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "drugs",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "interactions_found",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "interactions_found",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "formulary_issues",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "formulary_issues",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "source_file",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "source_file",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "FlaggedPrescriptions",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit#gid=FlaggedPrescriptions",
          "cachedResultName": "FlaggedPrescriptions"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit?usp=drivesdk",
          "cachedResultName": "Pharmacy Database"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "96e10c9a-46bc-4c60-9498-2bc958a5c752",
      "name": "Log Cleared Prescription to Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "notes": "Appends clean prescriptions + SOAP note to the Prescriptions database",
      "position": [
        -480,
        3296
      ],
      "parameters": {
        "columns": {
          "value": {
            "drugs": "={{ $json.drugs[0].name }}",
            "doctor": "={{ $json.prescribing_doctor }}",
            "timestamp": "={{ $json.extracted_at }}",
            "patient_id": "={{ $json.patient_id }}",
            "source_file": "={{ $json.source_file_name }}",
            "drug_details": "={{ $json.drugs }}",
            "patient_name": "={{ $json.patient_name }}",
            "prescription_date": "={{ $json.date }}"
          },
          "schema": [
            {
              "id": "timestamp",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "patient_name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "patient_name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "patient_id",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "patient_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "doctor",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "doctor",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "prescription_date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "prescription_date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "drugs",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "drugs",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "drug_details",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "drug_details",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "source_file",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "source_file",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "Prescriptions",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit#gid=Prescriptions",
          "cachedResultName": "Prescriptions"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEET_ID",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_GOOGLE_SHEET_ID/edit?usp=drivesdk",
          "cachedResultName": "Pharmacy Database"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "e16ae0ad-1943-4718-920f-a9f5810c4a04",
      "name": "Extract Text from PDF",
      "type": "n8n-nodes-base.extractFromFile",
      "notes": "Extracts raw text from the prescription PDF. Outputs $json.text which feeds directly into GPT-4o.",
      "position": [
        -2576,
        3088
      ],
      "parameters": {
        "options": {},
        "operation": "pdf"
      },
      "typeVersion": 1.1
    },
    {
      "id": "49a811e2-b6df-416a-8ec8-be4f11b8ac28",
      "name": "Merge Formulary & Interactions",
      "type": "n8n-nodes-base.merge",
      "position": [
        -1424,
        3072
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "ab675d01-5d64-484a-b97e-15bf31bbd014",
      "name": "On Workflow Error",
      "type": "n8n-nodes-base.errorTrigger",
      "position": [
        -2944,
        3680
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "f9ab689c-e832-42fe-b5d1-c45f2c4abf0f",
      "name": "Slack \u2013 Send Error Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        -2688,
        3680
      ],
      "parameters": {
        "text": "=\u26a0\ufe0f Prescription workflow error: {{ $json.error.message }}\nNode: {{ $json.execution.lastNodeExecuted }}\nExecution ID: {{ $json.execution.id }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SLACK_CHANNEL_ID",
          "cachedResultName": "pharmacy-alerts"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "typeVersion": 2.3
    },
    {
      "id": "eca3e0f7-1aec-44cc-a994-f97c09792d35",
      "name": "Section: Error Handler",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3040,
        3520
      ],
      "parameters": {
        "color": 7,
        "width": 556,
        "height": 368,
        "content": "## \u26a0\ufe0f Error Handler\nCatches any failure in the workflow and posts a Slack alert with the error message, failing node name, and execution ID. Wire the error output of any critical node here to prevent silent failures going unnoticed."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "8a8237f8-c891-4ede-acf3-4546123f2663",
  "connections": {
    "On Workflow Error": {
      "main": [
        [
          {
            "node": "Slack \u2013 Send Error Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Text from PDF": {
      "main": [
        [
          {
            "node": "GPT-4o \u2013 Extract + Draft SOAP Note",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download PDF from Drive": {
      "main": [
        [
          {
            "node": "Extract Text from PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route: Flagged or Cleared?": {
      "main": [
        [
          {
            "node": "Telegram \u2013 Alert Pharmacist",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Cleared Prescription to Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Watch Prescriptions Folder": {
      "main": [
        [
          {
            "node": "Download PDF from Drive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Approved Drug Formulary": {
      "main": [
        [
          {
            "node": "Merge Formulary & Interactions",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Drug Interactions Table": {
      "main": [
        [
          {
            "node": "Merge Formulary & Interactions",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Parse & Validate GPT Response": {
      "main": [
        [
          {
            "node": "Read Approved Drug Formulary",
            "type": "main",
            "index": 0
          },
          {
            "node": "Read Drug Interactions Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram \u2013 Alert Pharmacist": {
      "main": [
        [
          {
            "node": "Log Flagged Prescription to Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Formulary & Interactions": {
      "main": [
        [
          {
            "node": "Check Formulary & Flag Interactions",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Cleared Prescription to Sheets": {
      "main": [
        [
          {
            "node": "Telegram \u2013 Notify Patient",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Formulary & Flag Interactions": {
      "main": [
        [
          {
            "node": "Route: Flagged or Cleared?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GPT-4o \u2013 Extract + Draft SOAP Note": {
      "main": [
        [
          {
            "node": "Parse & Validate GPT Response",
            "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 monitors a Google Drive folder for new prescription PDFs, extracts the text, uses OpenAI (GPT-4o-mini) to structure prescription details and draft a SOAP note, checks drugs against Google Sheets formulary and interaction tables, and notifies a pharmacist or patient…

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

More Slack & Telegram workflows → · Browse all categories →

Related workflows

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

Slack & Telegram

This workflow collects patient symptoms via an n8n Form, uses OpenAI (GPT-4o Mini) to draft a SOAP note, and sends it to a doctor for email approval in Gmail. Approved notes are saved to Google Sheets

Form Trigger, OpenAI, Gmail +4
Slack & Telegram

google drive to instagram, tiktok and youtube. Uses googleDriveTrigger, googleDrive, errorTrigger, telegram. Event-driven trigger; 15 nodes.

Google Drive Trigger, Google Drive, Error Trigger +5
Slack & Telegram

This workflow is designed for business analysts, market researchers, lead generation specialists, and sales teams who need to gather detailed business intelligence from Google Maps. It's particularly

Telegram, OpenAI, N8N Nodes Apify +5
Slack & Telegram

This workflow uses Google Sheets, OpenAI, Telegram, and Slack to send post-therapy check-in messages, analyze patient replies for sentiment and risk, alert therapists to at-risk responses, and log non

Google Sheets, OpenAI, Telegram +3
Slack & Telegram

This template monitors Google Drive folder for new files, extracts text from PDFs, images, text files, CSVs, and Google Docs., reads images with meta/llama-3.2-11b-vision-instruct, structures the resu

Google Drive Trigger, Google Drive, Google Docs +3