AutomationFlowsSlack & Telegram › Manage Clinic Appointments and Prescriptions with Telegram, Gmail and Google

Manage Clinic Appointments and Prescriptions with Telegram, Gmail and Google

ByRahul Joshi @rahul08 on n8n.io

This workflow manages clinic appointments across Google Sheets and Google Calendar, using webhooks and Telegram to book and confirm visits, Gmail and Telegram to deliver prescriptions, scheduled Telegram reminders for upcoming appointments, and Slack notifications for workflow…

Event trigger★★★★★ complexity32 nodesGoogle CalendarTelegramGoogle SheetsTelegram TriggerGmailError TriggerSlack
Slack & Telegram Trigger: Event Nodes: 32 Complexity: ★★★★★ Added:
Manage Clinic Appointments and Prescriptions with Telegram, Gmail and Google — n8n workflow card showing Google Calendar, Telegram, Google Sheets integration

This workflow corresponds to n8n.io template #16878 — 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": "hMV1BCjqDGfBtat1",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "AI Patient Appointment & Prescription Management System",
  "tags": [],
  "nodes": [
    {
      "id": "7f56cc54-59c1-4b94-a7ed-b5482e203ef9",
      "name": "\ud83d\udccc Overview \u2013 How This Workflow Works",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -256,
        1136
      ],
      "parameters": {
        "color": 2,
        "width": 892,
        "height": 644,
        "content": "## \ud83c\udfe5 ClinicOS \u2013 Appointment Booking & Patient Notification\n\n### How it works\nThis workflow handles the full patient appointment lifecycle across four independent pipelines. A webhook accepts new bookings from your clinic's frontend, logs them to Google Sheets, creates a calendar event, and notifies the doctor via Telegram. Patients can then confirm their appointment by sending their Booking ID to the clinic's Telegram bot \u2014 the workflow validates it, updates the sheet, and alerts the doctor. A separate prescription pipeline delivers medication details by email and Telegram once a doctor submits post-visit notes. An hourly schedule also checks for appointments due the next day and sends automatic reminders.\n\n### Setup steps\n1. Connect **Telegram API** to all Telegram nodes using your clinic bot token.\n2. Connect **Google Sheets OAuth2** \u2014 create a sheet with a tab named `clinicos_appointments` and add all required column headers.\n3. Connect **Google Calendar OAuth2** and select your clinic's calendar.\n4. Connect **Gmail OAuth2** for prescription email delivery.\n5. Connect **Slack OAuth2** and set your alert channel ID for error notifications.\n6. Replace the hardcoded doctor Telegram Chat ID in the Reminder node with `{{ $json['Doctor Chat ID'] }}` or your clinic's actual chat ID.\n7. Activate all three triggers (Webhook, Telegram Trigger, Schedule)."
      },
      "typeVersion": 1
    },
    {
      "id": "a9f47165-6e1d-42df-a202-d1e0271c1f2d",
      "name": "Section \u2013 Appointment Booking",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        944,
        1568
      ],
      "parameters": {
        "color": 7,
        "width": 1072,
        "height": 456,
        "content": "## \ud83d\udce5 Appointment Booking\nReceives a POST request from your clinic frontend, generates a unique Booking ID, appends the appointment to Google Sheets, and immediately notifies the doctor via Telegram with full patient details."
      },
      "typeVersion": 1
    },
    {
      "id": "02a07d3b-4afe-4d48-a517-b12222255785",
      "name": "Section \u2013 Calendar Event",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2144,
        1616
      ],
      "parameters": {
        "color": 7,
        "width": 348,
        "height": 456,
        "content": "## \ud83d\udcc5 Calendar Event\nCreates a Google Calendar event for confirmed appointments so the doctor's schedule stays in sync automatically."
      },
      "typeVersion": 1
    },
    {
      "id": "03ad89f5-1978-404a-b7ed-ab0d5dea2e42",
      "name": "Section \u2013 Patient Confirmation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1056,
        2128
      ],
      "parameters": {
        "color": 7,
        "width": 1616,
        "height": 744,
        "content": "## \u2705 Patient Confirmation\nPatient replies to the Telegram bot with their Booking ID (e.g. `CONFIRM BK-1001`). The workflow extracts and validates the ID, marks the appointment Confirmed in Google Sheets, notifies the patient, then fetches the full appointment row and alerts the doctor."
      },
      "typeVersion": 1
    },
    {
      "id": "dc10e25a-b000-47f6-8389-d2b0d7708e5f",
      "name": "Section \u2013 Prescription Delivery",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1264,
        2944
      ],
      "parameters": {
        "color": 7,
        "width": 1248,
        "height": 472,
        "content": "## \ud83d\udc8a Prescription Delivery\nTriggered by a webhook when the doctor submits post-visit notes. Updates the appointment status, emails the patient their prescription PDF link, and sends a Telegram message listing prescribed medicines."
      },
      "typeVersion": 1
    },
    {
      "id": "57805a3a-c87b-4071-932e-bb961b9fee83",
      "name": "Section \u2013 Appointment Reminders",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1360,
        3472
      ],
      "parameters": {
        "color": 7,
        "width": 996,
        "height": 424,
        "content": "## \u23f0 Appointment Reminders\nRuns every hour. Reads all booked appointments, filters any due within 24 hours, and sends a Telegram reminder to each patient. Adjust the time window in the Filter node for production use."
      },
      "typeVersion": 1
    },
    {
      "id": "589da631-55fc-4807-90f4-ee9e924a1362",
      "name": "Section \u2013 Error Handling",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        192,
        2656
      ],
      "parameters": {
        "color": 7,
        "width": 556,
        "height": 416,
        "content": "## \u26a0\ufe0f Error Handling\nA workflow-level error trigger posts an alert to Slack whenever any node fails. Update the channel ID to your ops or alerts channel before activating."
      },
      "typeVersion": 1
    },
    {
      "id": "f4a6fc83-386a-42c6-a5b8-85b1ba8a0207",
      "name": "\ud83d\udd11 Credentials & Security",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2784,
        3008
      ],
      "parameters": {
        "color": 3,
        "width": 492,
        "height": 316,
        "content": "## \ud83d\udd11 Credentials Required\n- **Telegram API** \u2014 clinic bot token (all Telegram nodes)\n- **Google Sheets OAuth2** \u2014 appointment log\n- **Google Calendar OAuth2** \u2014 clinic calendar\n- **Gmail OAuth2** \u2014 prescription emails\n- **Slack OAuth2** \u2014 error alerts\n\nNever hardcode personal emails, chat IDs, or credential tokens in shared templates."
      },
      "typeVersion": 1
    },
    {
      "id": "bf5797f0-fd25-40b7-8a44-96038a4ed143",
      "name": "\ud83d\udcc5 Create Google Calendar Event",
      "type": "n8n-nodes-base.googleCalendar",
      "position": [
        2256,
        1856
      ],
      "parameters": {
        "calendar": {
          "__rl": true,
          "mode": "list",
          "value": "user@example.com",
          "cachedResultName": "Clinic Google Calendar"
        },
        "additionalFields": {
          "description": "=patient name - {{ $('\ud83d\udce8 Telegram \u2013 Patient Sends Confirm').item.json.message.from.first_name }} status - {{ $json.Status }}"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "4cf95d3b-cdc9-414e-b52d-b463d46670a8",
      "name": "\ud83d\udcf2 Telegram \u2013 Notify Doctor",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2480,
        2352
      ],
      "parameters": {
        "text": "=\u2705 *Patient Confirmed Appointment*\n\n\ud83d\udc64 *Patient:* {{ $json['Patient Name'] }}\n\ud83d\udcde *Phone:* {{ $json['Patient Phone'] }}\n\ud83d\udcc5 *Date:* {{ $json['Appointment Date'] }}\n\u23f0 *Time:* {{ $json['Appointment Time'] }}\n\ud83d\udccb *Reason:* {{ $json['Reason'] }}\n\ud83d\udd16 *Booking ID:* {{ $json['Booking ID'] }}\n\nPatient is confirmed. Please prepare for the visit. \ud83d\udc68\u200d\u2695\ufe0f",
        "chatId": "={{ $('\ud83d\udce8 Telegram \u2013 Patient Sends Confirm').item.json.message.from.id }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "68d0abe4-50d3-4711-ba9c-91654b2cc22a",
      "name": "\ud83d\udcca Update Status to Confirmed",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1808,
        2352
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "Confirmed",
            "Booking ID": "={{ $json.bookingId }}"
          },
          "schema": [
            {
              "id": "Booking ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Booking ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Patient Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Patient Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Patient Email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Patient Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Patient Phone",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Patient Phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Doctor Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Doctor Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Doctor Chat ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Doctor Chat ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Appointment Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Appointment Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Appointment Time",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Appointment Time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Reason",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Reason",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Created At",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Created At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Booking ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "clinicos_appointments"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "1748ddde-ec0b-4041-b44e-41543cf37167",
      "name": "\ud83d\udcf2 Telegram \u2013 Invalid ID Reply",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1856,
        2672
      ],
      "parameters": {
        "text": "=\u274c Sorry, I couldn't find a valid Booking ID in your message.\n\nPlease reply with your Booking ID in this format:\n`CONFIRM BK-1001`",
        "chatId": "={{ $json.patientChatId }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "c93b18eb-0d56-4f6b-b84a-341ab78b6afe",
      "name": "\ud83d\udccb Read Appointment Row",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        2256,
        2352
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "clinicos_appointments"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "96bcb98b-36f9-4474-a53e-95a5a8d1adda",
      "name": "\ud83d\udcf2 Telegram \u2013 Confirm to Patient",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2032,
        2352
      ],
      "parameters": {
        "text": "=\u2705 *Appointment Confirmed!*\n\nHi {{ $('\u2699\ufe0f Extract Booking ID from Message').item.json.patientName }}, your appointment *{{ $('\u2699\ufe0f Extract Booking ID from Message').item.json.bookingId }}* has been confirmed.\n\nWe'll see you at the clinic. Please arrive 10 minutes early. \ud83c\udfe5",
        "chatId": "={{ $('\u2699\ufe0f Extract Booking ID from Message').item.json.patientChatId }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "c01e79c1-737a-43cc-a784-f3dcd848d38b",
      "name": "\ud83d\udd00 Valid Booking ID?",
      "type": "n8n-nodes-base.if",
      "position": [
        1584,
        2480
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "254a0257-986f-4035-a08a-c5cc1ecb1061",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.valid }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "3c66b096-76fc-4027-83c2-b6519da0a111",
      "name": "\u2699\ufe0f Extract Booking ID from Message",
      "type": "n8n-nodes-base.code",
      "position": [
        1360,
        2480
      ],
      "parameters": {
        "jsCode": "const msg = $input.first().json.message;\nconst text = msg?.text || '';\nconst chatId = msg?.chat?.id?.toString() || '';\nconst from = msg?.from?.first_name || 'Patient';\n\n// Patient sends message like: CONFIRM BK-1001\n// Extract booking ID from message text\nconst match = text.match(/BK-[\\w]+/i);\nconst bookingId = match ? match[0].toUpperCase() : null;\n\nif (!bookingId) {\n  return [{ json: { valid: false, chatId, from, text } }];\n}\n\nreturn [{ json: { valid: true, bookingId, patientChatId: chatId, patientName: from, rawText: text } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "067abca9-e43f-411a-96af-cdca707dd1d9",
      "name": "\ud83d\udce8 Telegram \u2013 Patient Sends Confirm",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        1136,
        2480
      ],
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "typeVersion": 1.1
    },
    {
      "id": "ca2dbd39-50c6-48b2-8d22-5dc95e30951b",
      "name": "\ud83d\udcf2 Telegram \u2013 Send Reminder",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2128,
        3664
      ],
      "parameters": {
        "text": "=\u23f0 *Appointment Reminder \u2013 Tomorrow*\n\n\ud83d\udc64 *Patient:* {{ $json['Patient Name'] }}\n\ud83d\udcde *Phone:* {{ $json['Patient Phone'] }}\n\ud83d\udce7 *Email:* {{ $json['Patient Email'] }}\n\ud83d\udcc5 *Date:* {{ $json['Appointment Date'] }}\n\u23f0 *Time:* {{ $json['Appointment Time'] }}\n\ud83d\udccb *Reason:* {{ $json['Reason'] }}\n\ud83d\udd16 *Booking ID:* {{ $json['Booking ID'] }}\n\nPlease confirm attendance or reschedule.",
        "chatId": "={{ $json['Doctor Chat ID'] }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "9b68e758-0524-43bf-90ef-5bc2904b9dfb",
      "name": "\ud83d\udd0d Filter \u2013 Due for Reminder",
      "type": "n8n-nodes-base.code",
      "position": [
        1904,
        3664
      ],
      "parameters": {
        "jsCode": "const now = Date.now();\nconst items = $input.all();\n\nconst toRemind = items.filter(item => {\n  const row = item.json;\n  if (!row['Appointment Date'] || !row['Appointment Time']) return false;\n  if (row['Status'] !== 'Booked') return false;\n\n  const dtStr = `${row['Appointment Date']}T${row['Appointment Time']}:00`;\n  const apptMs = new Date(dtStr).getTime();\n  const reminderMs = apptMs - 24 * 60 * 60 * 1000;\n\n  // \u2b07\ufe0f TEST MODE: passes anything in next 7 days instead of exact 1hr window\n  return reminderMs >= now && reminderMs < now + 7 * 24 * 60 * 60 * 1000;\n});\n\nif (toRemind.length === 0) return [];\nreturn toRemind.map(item => ({ json: item.json }));"
      },
      "typeVersion": 2
    },
    {
      "id": "34fb5470-cbd9-44fa-a02d-2044097f9fd2",
      "name": "\ud83d\udccb Read Booked Appointments",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1680,
        3664
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "clinicos_appointments"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "675e4cb1-383a-4508-870b-d0a626eb43d7",
      "name": "\u23f0 Hourly Reminder Check",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1456,
        3664
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours"
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "27d7dcdc-384e-4163-a62d-3c890cb50b21",
      "name": "\ud83d\udcf2 Telegram \u2013 Booking Confirmed",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1760,
        1792
      ],
      "parameters": {
        "text": "=\ud83c\udfe5 *New Appointment Booked*\n\n\ud83d\udc64 *Patient:* {{ $('\u2699\ufe0f Prepare Booking Data').item.json.patientName }}\n\ud83d\udcde *Phone:* {{ $('\u2699\ufe0f Prepare Booking Data').item.json.patientPhone }}\n\ud83d\udce7 *Email:* {{ $('\u2699\ufe0f Prepare Booking Data').item.json.patientEmail }}\n\ud83d\udc68\u200d\u2695\ufe0f *Doctor:* {{ $('\u2699\ufe0f Prepare Booking Data').item.json.doctorName }}\n\ud83d\udcc5 *Date:* {{ $('\u2699\ufe0f Prepare Booking Data').item.json.appointmentDate }}\n\u23f0 *Time:* {{ $('\u2699\ufe0f Prepare Booking Data').item.json.appointmentTime }}\n\ud83d\udccb *Reason:* {{ $('\u2699\ufe0f Prepare Booking Data').item.json.reason }}\n\ud83d\udd16 *Booking ID:* {{ $('\u2699\ufe0f Prepare Booking Data').item.json.bookingId }}",
        "chatId": "={{ $('\u2699\ufe0f Prepare Booking Data').item.json.doctorChatId }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "f51465c9-1eea-47d2-988a-e4a1417c2c11",
      "name": "\ud83d\udcca Append to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1504,
        1792
      ],
      "parameters": {
        "columns": {
          "value": {
            "Reason": "={{ $json.reason }}",
            "Status": "={{ $json.status }}",
            "Booking ID": "={{ $json.bookingId }}",
            "Created At": "={{ $json.createdAt }}",
            "Doctor Name": "={{ $json.doctorName }}",
            "Patient Name": "={{ $json.patientName }}",
            "Patient Email": "={{ $json.patientEmail }}",
            "Patient Phone": "={{ $json.patientPhone }}",
            "Doctor Chat ID": "={{ $json.doctorChatId }}",
            "Appointment Date": "={{ $json.appointmentDate }}",
            "Appointment Time": "={{ $json.appointmentTime }}"
          },
          "schema": [
            {
              "id": "Booking ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Booking ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Patient Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Patient Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Patient Email",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Patient Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Patient Phone",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Patient Phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Doctor Name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Doctor Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Doctor Chat ID",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Doctor Chat ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Appointment Date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Appointment Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Appointment Time",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Appointment Time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Reason",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Reason",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Created At",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Created At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "clinicos_appointments"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "3115bd12-9795-487c-af1f-bce93518a23c",
      "name": "\u2699\ufe0f Prepare Booking Data",
      "type": "n8n-nodes-base.code",
      "position": [
        1264,
        1792
      ],
      "parameters": {
        "jsCode": "const body = $input.first().json.body || $input.first().json;\n\nconst name = body.patient_name || 'Unknown';\nconst email = body.patient_email || '';\nconst phone = body.patient_phone || '';\nconst doctor = body.doctor_name || 'Dr. Smith';\nconst doctorChatId = body.doctor_chat_id || '';\nconst appointmentDate = body.appointment_date || '';\nconst appointmentTime = body.appointment_time || '';\nconst reason = body.reason || 'General Checkup';\n\nconst dtString = `${appointmentDate}T${appointmentTime}:00`;\nconst apptMs = new Date(dtString).getTime();\nconst followupDatetime = new Date(apptMs + 24 * 60 * 60 * 1000).toISOString();\nconst bookingId = 'BK-' + Date.now();\n\nreturn [{\n  json: {\n    bookingId,\n    patientName: name,\n    patientEmail: email,\n    patientPhone: phone,\n    doctorName: doctor,\n    doctorChatId,\n    appointmentDate,\n    appointmentTime,\n    appointmentDatetime: dtString,\n    followupDatetime,\n    reason,\n    status: 'Booked',\n    createdAt: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "976d17b8-4217-446f-8199-7776f76ac88c",
      "name": "\ud83d\udce5 Patient Books Appointment",
      "type": "n8n-nodes-base.webhook",
      "position": [
        1040,
        1792
      ],
      "parameters": {
        "path": "clinic-book",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2
    },
    {
      "id": "46eae04c-089f-457e-b3fe-45cc5ae85abc",
      "name": "\ud83d\udc8a Prescription Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        1344,
        3168
      ],
      "parameters": {
        "path": "clinic-prescription",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2
    },
    {
      "id": "ab726e8c-133b-46ce-aa9e-6477d09eb1fe",
      "name": "\u2699\ufe0f Extract Prescription Data",
      "type": "n8n-nodes-base.code",
      "position": [
        1568,
        3168
      ],
      "parameters": {
        "jsCode": "const body = $input.first().json.body || $input.first().json;\n\nreturn [{\n  json: {\n    bookingId: body.booking_id || '',\n    patientName: body.patient_name || 'Patient',\n    patientEmail: body.patient_email || '',\n    patientChatId: body.patient_chat_id || '',\n    doctorName: body.doctor_name || 'Your Doctor',\n    prescriptionLink: body.prescription_link || '',\n    medicines: body.medicines || 'Please check your prescription for medicine details'\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "091fe634-e17c-4a14-b1ac-7c61c32ee90b",
      "name": "\ud83d\udcca Update Sheet \u2013 Prescription Sent",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1792,
        3168
      ],
      "parameters": {
        "columns": {
          "value": {
            "Status": "Prescription Sent",
            "Booking ID": "={{ $json.bookingId }}",
            "row_number": 0,
            "prescription link": "={{ $json.prescriptionLink }}"
          },
          "schema": [
            {
              "id": "Booking ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Booking ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Patient Name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Patient Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Patient Email",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Patient Email",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Patient Phone",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Patient Phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Doctor Name",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Doctor Name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Doctor Chat ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Doctor Chat ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Appointment Date",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Appointment Date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Appointment Time",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Appointment Time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Reason",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Reason",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Status",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Status",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "prescription link",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "prescription link",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Created At",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "Created At",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": true,
              "required": false,
              "displayName": "row_number",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "Booking ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "clinicos_appointments"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "581663bb-dd60-49ab-b8a8-70a1ae721a6a",
      "name": "\ud83d\udce7 Gmail \u2013 Send Prescription",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2016,
        3168
      ],
      "parameters": {
        "sendTo": "={{ $('\u2699\ufe0f Extract Prescription Data').item.json.patientEmail }}",
        "message": "=<div style=\"font-family:Arial,sans-serif;max-width:600px;margin:auto;padding:24px;border:1px solid #e5e7eb;border-radius:8px\">\n  <h2 style=\"color:#7c3aed\">Your Prescription is Ready \ud83d\udc8a</h2>\n  <p>Hi <strong>{{ $('\u2699\ufe0f Extract Prescription Data').item.json.patientName }}</strong>,</p>\n  <p>Dr. <strong>{{ $('\u2699\ufe0f Extract Prescription Data').item.json.doctorName }}</strong> has shared your prescription after your recent visit.</p>\n\n  <div style=\"background:#f5f3ff;border-left:4px solid #7c3aed;padding:16px;border-radius:4px;margin:20px 0\">\n    <p style=\"margin:0 0 8px 0\"><strong>\ud83d\udc8a Prescribed Medicines:</strong></p>\n    <p style=\"margin:0;color:#374151\">{{ $('\u2699\ufe0f Extract Prescription Data').item.json.medicines }}</p>\n  </div>\n\n  <p style=\"margin:20px 0\">\n    <a href=\"{{ $('\u2699\ufe0f Extract Prescription Data').item.json.prescriptionLink }}\" \n       style=\"background:#7c3aed;color:#fff;padding:12px 24px;border-radius:6px;text-decoration:none;font-weight:bold;display:inline-block\">\n      \ud83d\udcc4 View Full Prescription\n    </a>\n  </p>\n\n  <p style=\"color:#374151\">Please take your medicines as prescribed. If you have any questions, reply to this email or contact the clinic.</p>\n  <p style=\"color:#6b7280;font-size:13px;margin-top:24px\">Booking ID: {{ $('\u2699\ufe0f Extract Prescription Data').item.json.bookingId }} &nbsp;|&nbsp; ClinicOS AI</p>\n</div>",
        "options": {},
        "subject": "=\ud83d\udc8a Your Prescription from {{ $('\u2699\ufe0f Extract Prescription Data').item.json.doctorName }} \u2013 Booking {{ $('\u2699\ufe0f Extract Prescription Data').item.json.bookingId }}"
      },
      "typeVersion": 2.1
    },
    {
      "id": "fa3152cc-a2a5-4818-8ded-683482873b9b",
      "name": "\ud83d\udcf2 Telegram \u2013 Medicine Order to Patient",
      "type": "n8n-nodes-base.telegram",
      "position": [
        2240,
        3168
      ],
      "parameters": {
        "text": "=\ud83d\udc8a *Your Prescription & Medicine Order*\n\nHi {{ $('\u2699\ufe0f Extract Prescription Data').item.json.patientName }}, your prescription from *{{ $('\u2699\ufe0f Extract Prescription Data').item.json.doctorName }}* is ready!\n\n\ud83e\uddfe *Medicines Prescribed:*\n{{ $('\u2699\ufe0f Extract Prescription Data').item.json.medicines }}\n\n\ud83d\udcc4 *Full Prescription:* {{ $('\u2699\ufe0f Extract Prescription Data').item.json.prescriptionLink }}\n\n\ud83d\uded2 Please order your medicines at the earliest. You can show this message at any pharmacy.\n\n_Booking ID: {{ $('\u2699\ufe0f Extract Prescription Data').item.json.bookingId }}_",
        "chatId": "={{ $('\u2699\ufe0f Extract Prescription Data').item.json.patientChatId }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "cf5770a4-9ea3-47dc-81de-2c49f8ca09c2",
      "name": "On Workflow Error",
      "type": "n8n-nodes-base.errorTrigger",
      "position": [
        256,
        2880
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "433dec7d-27d2-40e8-9513-7c01c0fdf688",
      "name": "Slack \u2013 Post Workflow Error Alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        512,
        2880
      ],
      "parameters": {
        "text": "=\ud83d\udea8 *ClinicOS Workflow Error*\n\n*Workflow:* {{ $workflow.name }}\n*Failed Node:* {{ $execution.lastNodeExecuted }}\n*Time:* {{ $now.format('MMM D, YYYY \u2013 HH:mm') }}\n*Execution ID:* {{ $execution.id }}\n\nPlease check the n8n execution log and follow up with the patient manually if needed.",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_SLACK_CHANNEL_ID",
          "cachedResultName": "your-alerts-channel"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "typeVersion": 2.3
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "9dfac977-51b4-4b07-9f16-2b4b56848954",
  "nodeGroups": [],
  "connections": {
    "On Workflow Error": {
      "main": [
        [
          {
            "node": "Slack \u2013 Post Workflow Error Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd00 Valid Booking ID?": {
      "main": [
        [
          {
            "node": "\ud83d\udcca Update Status to Confirmed",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\ud83d\udcf2 Telegram \u2013 Invalid ID Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u23f0 Hourly Reminder Check": {
      "main": [
        [
          {
            "node": "\ud83d\udccb Read Booked Appointments",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udc8a Prescription Webhook": {
      "main": [
        [
          {
            "node": "\u2699\ufe0f Extract Prescription Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udccb Read Appointment Row": {
      "main": [
        [
          {
            "node": "\ud83d\udcf2 Telegram \u2013 Notify Doctor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2699\ufe0f Prepare Booking Data": {
      "main": [
        [
          {
            "node": "\ud83d\udcca Append to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcca Append to Google Sheets": {
      "main": [
        [
          {
            "node": "\ud83d\udcf2 Telegram \u2013 Booking Confirmed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udccb Read Booked Appointments": {
      "main": [
        [
          {
            "node": "\ud83d\udd0d Filter \u2013 Due for Reminder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce5 Patient Books Appointment": {
      "main": [
        [
          {
            "node": "\u2699\ufe0f Prepare Booking Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcca Update Status to Confirmed": {
      "main": [
        [
          {
            "node": "\ud83d\udcc5 Create Google Calendar Event",
            "type": "main",
            "index": 0
          },
          {
            "node": "\ud83d\udcf2 Telegram \u2013 Confirm to Patient",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2699\ufe0f Extract Prescription Data": {
      "main": [
        [
          {
            "node": "\ud83d\udcca Update Sheet \u2013 Prescription Sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce7 Gmail \u2013 Send Prescription": {
      "main": [
        [
          {
            "node": "\ud83d\udcf2 Telegram \u2013 Medicine Order to Patient",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd0d Filter \u2013 Due for Reminder": {
      "main": [
        [
          {
            "node": "\ud83d\udcf2 Telegram \u2013 Send Reminder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcf2 Telegram \u2013 Confirm to Patient": {
      "main": [
        [
          {
            "node": "\ud83d\udccb Read Appointment Row",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2699\ufe0f Extract Booking ID from Message": {
      "main": [
        [
          {
            "node": "\ud83d\udd00 Valid Booking ID?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcca Update Sheet \u2013 Prescription Sent": {
      "main": [
        [
          {
            "node": "\ud83d\udce7 Gmail \u2013 Send Prescription",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udce8 Telegram \u2013 Patient Sends Confirm": {
      "main": [
        [
          {
            "node": "\u2699\ufe0f Extract Booking ID from 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.

Pro

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

About this workflow

This workflow manages clinic appointments across Google Sheets and Google Calendar, using webhooks and Telegram to book and confirm visits, Gmail and Telegram to deliver prescriptions, scheduled Telegram reminders for upcoming appointments, and Slack notifications for workflow…

Source: https://n8n.io/workflows/16878/ — 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

clients kept booking meetings during my prayer times. i'd either miss a prayer or scramble to reschedule. the problem wasn't the clients — it was that my calendar had no blocked windows for salah. i n

Telegram Trigger, HTTP Request, Google Calendar +3
Slack & Telegram

This workflow lets customers order and refill prescriptions via Telegram by extracting medicines from prescription images with Google Gemini, checking and updating inventory in Google Sheets, and send

Telegram Trigger, Telegram, Google Gemini +3
Slack & Telegram

This workflow handles customer order complaints submitted via Telegram by looking up order details in Google Sheets, using OpenAI to classify message sentiment, and routing each case to auto-approval,

Telegram Trigger, Google Sheets, Telegram +3
Slack & Telegram

This workflow handles COD order confirmation and abandoned-cart recovery by combining webhooks, Google Sheets logging, OpenAI message generation, and Telegram messaging with inline buttons, plus Slack

Telegram Trigger, Google Sheets, Telegram +3
Slack & Telegram

This workflow runs a Telegram restaurant ordering flow that uses OpenAI to parse orders and suggest an upsell, sends a Razorpay payment link, confirms paid orders via a Razorpay webhook, logs orders a

Telegram Trigger, OpenAI, Telegram +3