{
  "id": "LLedB68QYILTwtWZ",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Send prescription PDFs to patients via WhatsApp",
  "tags": [],
  "nodes": [
    {
      "id": "c0cbcf8e-5b3f-474d-becb-d87129df8860",
      "name": "Template Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -576,
        -96
      ],
      "parameters": {
        "color": 4,
        "width": 680,
        "height": 1092,
        "content": "## \ud83d\udc89 Send Prescription PDFs to Patients via WhatsApp\n\n## Who's it for\nClinics and solo practitioners who want to send patients a clean, professional prescription PDF over WhatsApp right after a consultation, without typing it up in a separate document tool.\n\n## How it works\nStaff sends a WhatsApp message describing the prescription (patient name, doctor, medications, dosages, instructions). Gemini extracts the details into structured data. If anything required is missing or unclear, the workflow replies asking staff to resend with the missing details instead of generating an incomplete prescription. If everything is complete, it generates a formatted PDF, uploads it, sends it back to the patient on WhatsApp, and logs the record to Supabase for the clinic's files.\n\n## How to set up\n1. Connect your WhatsApp Business Cloud credentials to the trigger and both send nodes.\n2. Connect your Google Gemini API credentials to the Chat Model node.\n3. Connect your Supabase credentials and create a `prescriptions` table (see the sticky note near the Supabase nodes for the schema).\n4. Get an API key from your HTML-to-PDF provider (e.g. pdfapihub.com) and a file-hosting provider (e.g. uploadtourl.com).\n5. Open the Set Configuration node and fill in your Sheet/API details, staff notification number, and WhatsApp phone number ID.\n6. Activate the workflow.\n\n## Requirements\n- WhatsApp Business Cloud API access\n- Google Gemini API key\n- Supabase project\n- An HTML-to-PDF API and a file-upload/hosting API\n\n## How to customize\nEdit the PDF HTML template in the Build PDF HTML node to match your clinic's letterhead and branding. Adjust the required-fields logic in the Extract Prescription Details prompt if your clinic needs additional mandatory fields (e.g. patient age, allergies)."
      },
      "typeVersion": 1
    },
    {
      "id": "13ce712f-3806-4939-b81f-a00959fade7b",
      "name": "Trigger Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        144,
        208
      ],
      "parameters": {
        "color": 7,
        "width": 348,
        "height": 232,
        "content": "## 1\ufe0f\u20e3 Trigger\nFires on every incoming WhatsApp message. Connect your clinic's WhatsApp Business number here. This workflow assumes staff are the ones sending the prescription details in, not patients."
      },
      "typeVersion": 1
    },
    {
      "id": "f6c36b19-29d3-4ce3-8fdb-8b3d962c2c9c",
      "name": "Set Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        480,
        464
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "cfg-1",
              "name": "clinic_name",
              "type": "string",
              "value": "REPLACE_WITH_CLINIC_NAME"
            },
            {
              "id": "cfg-2",
              "name": "staff_notify_number",
              "type": "string",
              "value": "REPLACE_WITH_STAFF_WHATSAPP_NUMBER"
            },
            {
              "id": "cfg-3",
              "name": "whatsapp_phone_number_id",
              "type": "string",
              "value": "REPLACE_WITH_WHATSAPP_PHONE_NUMBER_ID"
            },
            {
              "id": "cfg-4",
              "name": "supabase_table_name",
              "type": "string",
              "value": "prescriptions"
            },
            {
              "id": "cfg-5",
              "name": "pdf_api_url",
              "type": "string",
              "value": "REPLACE_WITH_PDF_API_ENDPOINT"
            },
            {
              "id": "cfg-6",
              "name": "pdf_api_key",
              "type": "string",
              "value": "REPLACE_WITH_PDF_API_KEY"
            },
            {
              "id": "cfg-7",
              "name": "upload_api_url",
              "type": "string",
              "value": "REPLACE_WITH_UPLOAD_API_ENDPOINT"
            },
            {
              "id": "cfg-8",
              "name": "upload_api_key",
              "type": "string",
              "value": "REPLACE_WITH_UPLOAD_API_KEY"
            },
            {
              "id": "cfg-9",
              "name": "raw_message",
              "type": "string",
              "value": "={{ $json.messages[0].text.body }}"
            },
            {
              "id": "cfg-10",
              "name": "sender_number",
              "type": "string",
              "value": "={{ $json.messages[0].from }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "e9918055-efdc-4cb6-a517-40f5dbd6648a",
      "name": "AI Extraction Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        704,
        224
      ],
      "parameters": {
        "color": 7,
        "width": 384,
        "height": 220,
        "content": "## 2\ufe0f\u20e3 AI Extraction\nGemini extracts patient name, doctor name, and medications (with dosage and instructions). It also fills a missing_fields list for anything it couldn't confidently extract, that list drives the validation branch next."
      },
      "typeVersion": 1
    },
    {
      "id": "00f579ae-8d81-4143-a5d6-4d9cff8806bf",
      "name": "Extract Prescription Details",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        800,
        464
      ],
      "parameters": {
        "text": "=You are a medical scribe assistant. Read the WhatsApp message below, sent by clinic staff describing a prescription, and extract it into structured data.\n\nMessage: {{ $json.raw_message }}\n\nExtract: patient_name, doctor_name, and a list of medications (each with name, dosage, and instructions). If any required field (patient_name, or a medication's name or dosage) is missing or too unclear to use, add a short description of what's missing to the missing_fields list. If everything required is present, return an empty missing_fields list.",
        "batching": {},
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.8
    },
    {
      "id": "7fd0c4f7-d9d4-40f9-b013-b09793ee1868",
      "name": "Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        720,
        688
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "315b6aa1-4582-4039-bc3f-efffd0760436",
      "name": "Prescription Data Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        1008,
        688
      ],
      "parameters": {
        "schemaType": "manual",
        "inputSchema": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"patient_name\": {\n      \"type\": \"string\"\n    },\n    \"doctor_name\": {\n      \"type\": \"string\"\n    },\n    \"medications\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"object\",\n        \"properties\": {\n          \"name\": {\n            \"type\": \"string\"\n          },\n          \"dosage\": {\n            \"type\": \"string\"\n          },\n          \"instructions\": {\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"name\",\n          \"dosage\"\n        ]\n      }\n    },\n    \"missing_fields\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      }\n    }\n  },\n  \"required\": [\n    \"patient_name\",\n    \"medications\",\n    \"missing_fields\"\n  ]\n}"
      },
      "typeVersion": 1.2
    },
    {
      "id": "9d515c91-eb36-4ced-aee5-44d3fcf91b97",
      "name": "Validation Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1136,
        224
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 220,
        "content": "## 3\ufe0f\u20e3 Validate Before Generating\nThis is the key branch: if missing_fields is non-empty, the workflow does NOT generate a PDF. It asks staff to resend the missing details instead, so patients never receive an incomplete prescription."
      },
      "typeVersion": 1
    },
    {
      "id": "5e8c20b9-8ad4-4fb8-a93a-51b9dfa85cd1",
      "name": "Are Required Fields Complete?",
      "type": "n8n-nodes-base.if",
      "position": [
        1184,
        464
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "9505ea41-8ac0-40db-a182-b8264c7b35ae",
              "operator": {
                "type": "number",
                "operation": "equals"
              },
              "leftValue": "={{ $json.output.missing_fields.length }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "224ef15c-b244-42f6-a1b2-2245178210f2",
      "name": "Needs Review Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1440,
        752
      ],
      "parameters": {
        "color": 7,
        "width": 396,
        "height": 164,
        "content": "## \u274c If Incomplete\nReplies to staff listing exactly what's missing, then logs the attempt to Supabase with status 'needs_review' so nothing silently disappears."
      },
      "typeVersion": 1
    },
    {
      "id": "b145af62-f0ac-47ae-aa00-7b8da9a3eb9f",
      "name": "Notify Staff - Needs Review",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        1456,
        928
      ],
      "parameters": {
        "textBody": "=Couldn't generate the prescription, some details are missing: {{ $json.output.missing_fields.join(', ') }}. Please resend with this info included.",
        "operation": "send",
        "phoneNumberId": "={{ $('Set Configuration').item.json.whatsapp_phone_number_id }}",
        "additionalFields": {},
        "recipientPhoneNumber": "={{ $('Set Configuration').item.json.sender_number }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "42a4f0ea-31d6-4f14-9e88-e21a0b8e5894",
      "name": "Log Needs Review",
      "type": "n8n-nodes-base.supabase",
      "position": [
        1696,
        928
      ],
      "parameters": {
        "operation": "insert"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "9e93839d-3d71-43b8-a5f1-5a61fee140c2",
      "name": "PDF Build Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1584,
        224
      ],
      "parameters": {
        "color": 7,
        "width": 336,
        "height": 216,
        "content": "## \u2705 If Complete\nBuilds the prescription HTML, converts it to a PDF, uploads it, sends it to the patient, and logs it. Edit the HTML template here to match your clinic's letterhead."
      },
      "typeVersion": 1
    },
    {
      "id": "6e3bf863-c313-4686-9a47-66d73e836a6d",
      "name": "Build PDF HTML",
      "type": "n8n-nodes-base.set",
      "position": [
        1648,
        448
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "html-1",
              "name": "prescription_html",
              "type": "string",
              "value": "=<html><body style=\"font-family: Arial, sans-serif; padding: 24px;\"><h2>{{ $('Set Configuration').item.json.clinic_name }}</h2><p><strong>Patient:</strong> {{ $json.output.patient_name }}</p><p><strong>Doctor:</strong> {{ $json.output.doctor_name }}</p><h3>Medications</h3><ul>{{ $json.output.medications.map(m => `<li>${m.name} - ${m.dosage}. ${m.instructions || ''}</li>`).join('') }}</ul><p style=\"margin-top:32px; font-size:12px; color:#666;\">Generated {{ $now.toFormat('yyyy-MM-dd HH:mm') }}</p></body></html>"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "e433ae5b-be8d-4ace-8998-0e30d0f9edde",
      "name": "Generate Prescription PDF",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1984,
        448
      ],
      "parameters": {
        "url": "={{ $('Set Configuration').item.json.pdf_api_url }}",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({ html: $json.prescription_html }) }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "CLIENT-API-KEY",
              "value": "={{ $('Set Configuration').item.json.pdf_api_key }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "4e5b119d-b561-45f7-8f9a-a6c95d41b9f3",
      "name": "Upload PDF to Public URL",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2208,
        448
      ],
      "parameters": {
        "url": "={{ $('Set Configuration').item.json.upload_api_url }}",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "multipart-form-data",
        "sendHeaders": true,
        "bodyParameters": {
          "parameters": [
            {}
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "CLIENT-API-KEY",
              "value": "={{ $('Set Configuration').item.json.upload_api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "0c6cdbdc-c822-4741-ad23-a0d3b4338854",
      "name": "Upload Response Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2000,
        240
      ],
      "parameters": {
        "color": 3,
        "width": 328,
        "height": 176,
        "content": "\u26a0\ufe0f Verify field name\nThe expression below assumes your upload API returns the file's public link as `url`. Check your provider's actual response and update the expression in the next node if the field name differs."
      },
      "typeVersion": 1
    },
    {
      "id": "a64cda28-81ef-4dfc-ba91-1da7a5d63a6e",
      "name": "Send Prescription PDF to Patient",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        2464,
        448
      ],
      "parameters": {
        "operation": "send",
        "messageType": "document",
        "phoneNumberId": "={{ $('Set Configuration').item.json.whatsapp_phone_number_id }}",
        "additionalFields": {},
        "recipientPhoneNumber": "={{ $('Set Configuration').item.json.sender_number }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "067d457b-7144-4dc9-a093-364bcaea47d7",
      "name": "Log Prescription Sent",
      "type": "n8n-nodes-base.supabase",
      "position": [
        2704,
        448
      ],
      "parameters": {
        "operation": "insert"
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "df2a1dce-784a-4ed1-8c35-4f2c2354e44e",
      "name": "Supabase Schema Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2512,
        208
      ],
      "parameters": {
        "color": 7,
        "width": 380,
        "height": 216,
        "content": "## Supabase Schema\nCreate a `prescriptions` table with columns: patient_name (text), doctor_name (text), medications (jsonb), status (text), pdf_url (text), created_at (timestamptz, default now())."
      },
      "typeVersion": 1
    },
    {
      "id": "bda355ad-d938-44bf-a77e-f77f7eb38c98",
      "name": "New WhatsApp Message",
      "type": "n8n-nodes-base.whatsAppTrigger",
      "position": [
        224,
        464
      ],
      "parameters": {
        "options": {},
        "updates": [
          "messages"
        ]
      },
      "credentials": {
        "whatsAppTriggerApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "a9ffea77-ddbb-4519-9223-dc96b7c812e0",
  "nodeGroups": [],
  "connections": {
    "Build PDF HTML": {
      "main": [
        [
          {
            "node": "Generate Prescription PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Extract Prescription Details",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Set Configuration": {
      "main": [
        [
          {
            "node": "Extract Prescription Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "New WhatsApp Message": {
      "main": [
        [
          {
            "node": "Set Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prescription Data Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Extract Prescription Details",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Upload PDF to Public URL": {
      "main": [
        [
          {
            "node": "Send Prescription PDF to Patient",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Prescription PDF": {
      "main": [
        [
          {
            "node": "Upload PDF to Public URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify Staff - Needs Review": {
      "main": [
        [
          {
            "node": "Log Needs Review",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Prescription Details": {
      "main": [
        [
          {
            "node": "Are Required Fields Complete?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Are Required Fields Complete?": {
      "main": [
        [
          {
            "node": "Build PDF HTML",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Notify Staff - Needs Review",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Prescription PDF to Patient": {
      "main": [
        [
          {
            "node": "Log Prescription Sent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}