This workflow follows the Google Sheets → HTTP Request 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 →
{
"name": "MediFlow \u2014 Prise de Rendez-vous WhatsApp (Evolution API)",
"active": false,
"settings": {
"executionOrder": "v1"
},
"nodes": [
{
"id": "3cca0317-e1d3-4a91-a119-88e5c259097b",
"name": "\ud83d\udce5 Webhook Evolution API",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [
-4576,
2368
],
"parameters": {
"httpMethod": "POST",
"path": "mediflow-rdv",
"responseMode": "responseNode",
"options": {}
}
},
{
"id": "f5332879-f396-493f-9089-2fab2098528c",
"name": "\ud83d\udd0d Extraire Message & T\u00e9l\u00e9phone",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-4352,
2368
],
"parameters": {
"jsCode": "// Parse le payload Evolution API et extrait le num\u00e9ro + message\nconst body = $input.first().json.body;\nlet phone = '';\nlet message = '';\nlet messageType = 'text';\n\ntry {\n if (body.data) {\n phone = body.data.key?.remoteJid?.replace('@s.whatsapp.net', '') || '';\n message = body.data.message?.conversation ||\n body.data.message?.extendedTextMessage?.text || '';\n messageType = body.data.messageType || 'conversation';\n const isFromMe = body.data?.key?.fromMe || false;\n if (isFromMe) return [{ json: { skip: true, reason: 'message_sortant' } }];\n } else if (body.messages) {\n phone = body.messages[0]?.key?.remoteJid?.replace('@s.whatsapp.net', '') || '';\n message = body.messages[0]?.message?.conversation || '';\n if (body.messages[0]?.key?.fromMe) return [{ json: { skip: true, reason: 'message_sortant' } }];\n }\n} catch(e) {\n console.log('Parse error:', e.message);\n}\n\n// Ignorer les messages non-texte (audio, images)\nif (!message || messageType === 'audioMessage' || messageType === 'imageMessage') {\n return [{ json: { skip: true, reason: 'pas_texte', phone } }];\n}\n\nreturn [{ json: { phone, message, messageType, timestamp: new Date().toISOString() } }];"
}
},
{
"id": "f174cd85-9803-40c0-814f-b1040486cfbe",
"name": "\ud83d\udea6 Message valide ?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
-4128,
2368
],
"parameters": {
"conditions": {
"conditions": [
{
"leftValue": "={{ $json.skip }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "notEquals"
}
}
]
}
}
},
{
"id": "26eb3dfc-726a-4e1b-a1f2-7e3d273ea682",
"name": "\ud83d\udc64 Chercher Patient (Google Sheets)",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4,
"position": [
-3728,
2176
],
"alwaysOutputData": true,
"parameters": {
"documentId": {
"__rl": true,
"value": "YOUR_GOOGLE_SHEET_ID",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "PATIENTS",
"mode": "name"
},
"filtersUI": {
"values": [
{
"lookupColumn": "telephone",
"lookupValue": "={{ $('\ud83d\udd0d Extraire Message & T\u00e9l\u00e9phone').item.json.phone }}"
}
]
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"id": "6358dbec-c150-4149-bb0a-1e140f292976",
"name": "\ud83c\udd95 Patient connu ?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
-3520,
2176
],
"parameters": {
"conditions": {
"conditions": [
{
"leftValue": "={{ $('\ud83d\udc64 Chercher Patient (Google Sheets)').item.json.patient_id }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty"
}
}
]
}
}
},
{
"id": "d4126e7b-9824-4ae4-a107-f17de62095b2",
"name": "\u2795 Cr\u00e9er Nouveau Patient",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4,
"position": [
-3376,
2592
],
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "YOUR_GOOGLE_SHEET_ID",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "PATIENTS",
"mode": "name"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"telephone": "={{ $('\ud83d\udd0d Extraire Message & T\u00e9l\u00e9phone').item.json.phone }}",
"patient_id": "=PAT_{{ $now.toFormat('yyyyMMddHHmmss') }}",
"date_inscription": "={{ $now.toISO() }}"
}
}
}
},
{
"id": "6b13162a-897f-4fe1-b7b9-734f8e4b7413",
"name": "\ud83d\udc4b Envoyer Message Bienvenue",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-3168,
2624
],
"parameters": {
"method": "POST",
"url": "=https://YOUR_EVOLUTION_API_URL/message/sendText/YOUR_INSTANCE_NAME",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "number",
"value": "={{ $('\ud83d\udd0d Extraire Message & T\u00e9l\u00e9phone').item.json.phone }}"
},
{
"name": "text",
"value": "Bonjour et bienvenue chez MediFlow ! \ud83d\udc4b\n\nJe suis MediBot, votre assistant pour la prise de rendez-vous m\u00e9dicaux.\n\nComment puis-je vous aider aujourd'hui ? Vous pouvez me dire par exemple :\n- \"Je veux prendre un rendez-vous\"\n- \"Je veux annuler mon rendez-vous\"\n- \"J'ai une urgence m\u00e9dicale\""
}
]
}
},
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"id": "5449abe4-aa9a-48ce-91ed-ef2e811f5401",
"name": "\ud83d\udccb Lire \u00c9tat Conversation",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-3296,
2176
],
"parameters": {
"jsCode": "// R\u00e9cup\u00e8re l'\u00e9tat de conversation persist\u00e9 en Google Sheets\nconst patientData = $('\ud83d\udc64 Chercher Patient (Google Sheets)').item.json;\nconst messageData = $('\ud83d\udd0d Extraire Message & T\u00e9l\u00e9phone').item.json;\n\nconst etatConv = patientData.etat_conversation || 'LIBRE';\nconst creneauxProposes = patientData.creneaux_proposes || '';\n\nreturn [{\n json: {\n phone: messageData.phone,\n message: messageData.message,\n patient_id: patientData.patient_id || '',\n patient_prenom: patientData.prenom || '',\n etat_conversation: etatConv,\n creneaux_proposes: creneauxProposes\n }\n}];"
}
},
{
"id": "cf75af23-5df4-4204-b086-ec6c1d4acc28",
"name": "\u23f3 En attente de choix ?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
-3072,
2176
],
"parameters": {
"conditions": {
"conditions": [
{
"leftValue": "={{ $json.etat_conversation }}",
"rightValue": "ATTENTE_CRENEAU",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
}
}
},
{
"id": "64c5a635-42d6-45d6-a51b-5cbd49937397",
"name": "Pr\u00e9pare Requ\u00eate Claude",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-2944,
2912
],
"parameters": {
"jsCode": "// Construit le prompt Claude selon l'\u00e9tat de conversation\nconst etat = $('\ud83d\udccb Lire \u00c9tat Conversation').item.json.etat_conversation || 'LIBRE';\nconst message = $('\ud83d\udccb Lire \u00c9tat Conversation').item.json.message || '';\n\nconst body = {\n model: \"claude-haiku-4-5-20251001\",\n max_tokens: 600,\n system: `Tu es MediBot, l'assistant de prise de rendez-vous d'une clinique priv\u00e9e.\n\nServices : cardiologie, p\u00e9diatrie, g\u00e9n\u00e9raliste, gyn\u00e9cologie, dermatologie.\n\u00c9tat conversation : ${etat}\n\nR\u00c8GLES :\n1. RDV sans sp\u00e9cialit\u00e9 \u2192 retourne PRENDRE_RDV, specialite=null, liste les sp\u00e9cialit\u00e9s dans message_reponse\n2. \u00c9tat ATTENTE_SPECIALITE \u2192 le message est la sp\u00e9cialit\u00e9 choisie\n3. Sp\u00e9cialit\u00e9 pr\u00e9cis\u00e9e \u2192 retourne PRENDRE_RDV avec specialite renseign\u00e9e\n4. Urgence \u2192 retourne URGENCE\n5. Autre \u2192 retourne AUTRE avec r\u00e9ponse utile\n\nRetourne UNIQUEMENT un JSON valide :\n{\n \"intention\": \"PRENDRE_RDV\" | \"ANNULER_RDV\" | \"CONFIRMER_RDV\" | \"URGENCE\" | \"AUTRE\",\n \"specialite\": null | \"cardiologie\" | \"p\u00e9diatrie\" | \"g\u00e9n\u00e9raliste\" | \"gyn\u00e9cologie\" | \"dermatologie\",\n \"message_reponse\": \"message chaleureux en fran\u00e7ais\",\n \"demander_specialite\": false\n}`,\n messages: [{ role: \"user\", content: \"Message du patient : \" + message }]\n};\n\nreturn [{ json: { claudeBody: JSON.stringify(body) } }];"
}
},
{
"id": "177206be-e2e0-4625-997c-7481e8472dd2",
"name": "\ud83e\udd16 Claude \u2014 Analyser Intention",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
-2704,
2912
],
"parameters": {
"method": "POST",
"url": "https://api.anthropic.com/v1/messages",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "anthropicApi",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "anthropic-version",
"value": "2023-06-01"
},
{
"name": "content-type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.parse($json.claudeBody) }}",
"options": {
"timeout": 30000
}
},
"credentials": {
"anthropicApi": {
"name": "<your credential>"
}
}
},
{
"id": "68b7a017-3d1e-46b1-9b5b-e9e90f99d208",
"name": "\u2699\ufe0f Parser R\u00e9ponse Claude",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-2496,
2912
],
"parameters": {
"jsCode": "// Parse la r\u00e9ponse JSON de Claude et attache les donn\u00e9es patient\nconst response = $input.first().json;\nlet claudeData = {};\n\ntry {\n const rawText = response.content[0].text;\n const cleaned = rawText.replace(/```json|```/g, '').trim();\n claudeData = JSON.parse(cleaned);\n} catch(e) {\n claudeData = {\n intention: 'AUTRE',\n message_reponse: 'Bonjour ! Je suis MediBot, votre assistant. Comment puis-je vous aider ?',\n demander_specialite: false\n };\n}\n\nclaudeData.phone = $('\ud83d\udccb Lire \u00c9tat Conversation').item.json.phone;\nclaudeData.patient_id = $('\ud83d\udccb Lire \u00c9tat Conversation').item.json.patient_id;\nclaudeData.patient_prenom = $('\ud83d\udccb Lire \u00c9tat Conversation').item.json.patient_prenom;\n\nreturn [{ json: claudeData }];"
}
},
{
"id": "89870dba-36e7-42d1-bfc7-ab861990eae7",
"name": "\ud83d\udd00 Router par Intention",
"type": "n8n-nodes-base.switch",
"typeVersion": 3,
"position": [
-2304,
2848
],
"parameters": {
"rules": {
"values": [
{
"conditions": {
"conditions": [
{
"leftValue": "={{ $json.intention }}",
"rightValue": "URGENCE",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
}
},
{
"conditions": {
"conditions": [
{
"leftValue": "={{ $json.intention }}",
"rightValue": "PRENDRE_RDV",
"operator": {
"type": "string",
"operation": "equals"
}
},
{
"leftValue": "={{ $json.specialite }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
}
}
]
}
},
{
"conditions": {
"conditions": [
{
"leftValue": "={{ $json.intention }}",
"rightValue": "ANNULER_RDV",
"operator": {
"type": "string",
"operation": "equals"
}
}
]
}
}
]
},
"options": {
"fallbackOutput": "extra"
}
}
},
{
"id": "0b7fc34d-a5b3-4769-b30c-a9d98b16dd79",
"name": "\ud83d\udcc5 Cr\u00e9er RDV Confirm\u00e9",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4,
"position": [
-2016,
1024
],
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "YOUR_GOOGLE_SHEET_ID",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "RENDEZ-VOUS",
"mode": "name"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"rdv_id": "=RDV_{{ $now.toFormat('yyyyMMddHHmmss') }}",
"patient_id": "={{ $json.patient_id }}",
"patient_telephone": "={{ $json.phone }}",
"medecin_nom": "={{ $json.creneau_choisi.medecin_nom }}",
"specialite": "={{ $json.creneau_choisi.specialite }}",
"date_rdv": "={{ $json.creneau_choisi.date }}",
"heure_rdv": "={{ $json.creneau_choisi.heure_debut }}",
"lieu": "={{ $json.creneau_choisi.lieu || 'Clinique MediFlow' }}",
"statut": "CONFIRME",
"date_creation": "={{ $now.toISO() }}"
}
}
}
},
{
"id": "51a92ed2-12b3-4bbb-883f-be37ead1c0e2",
"name": "\u2705 R\u00e9ponse Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
752,
2480
],
"parameters": {
"respondWith": "json",
"responseBody": "{ \"status\": \"ok\" }",
"options": {
"responseCode": 200
}
}
}
],
"connections": {
"\ud83d\udce5 Webhook Evolution API": {
"main": [
[
{
"node": "\ud83d\udd0d Extraire Message & T\u00e9l\u00e9phone",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd0d Extraire Message & T\u00e9l\u00e9phone": {
"main": [
[
{
"node": "\ud83d\udea6 Message valide ?",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udea6 Message valide ?": {
"main": [
[
{
"node": "\ud83d\udc64 Chercher Patient (Google Sheets)",
"type": "main",
"index": 0
}
],
[
{
"node": "\u2705 R\u00e9ponse Webhook",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udc64 Chercher Patient (Google Sheets)": {
"main": [
[
{
"node": "\ud83c\udd95 Patient connu ?",
"type": "main",
"index": 0
}
]
]
},
"\ud83c\udd95 Patient connu ?": {
"main": [
[
{
"node": "\ud83d\udccb Lire \u00c9tat Conversation",
"type": "main",
"index": 0
}
],
[
{
"node": "\u2795 Cr\u00e9er Nouveau Patient",
"type": "main",
"index": 0
}
]
]
},
"\u2795 Cr\u00e9er Nouveau Patient": {
"main": [
[
{
"node": "\ud83d\udc4b Envoyer Message Bienvenue",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udc4b Envoyer Message Bienvenue": {
"main": [
[
{
"node": "\u2705 R\u00e9ponse Webhook",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udccb Lire \u00c9tat Conversation": {
"main": [
[
{
"node": "\u23f3 En attente de choix ?",
"type": "main",
"index": 0
}
]
]
},
"\u23f3 En attente de choix ?": {
"main": [
[
{
"node": "\ud83e\udd16 Claude \u2014 Analyser Intention",
"type": "main",
"index": 0
}
],
[
{
"node": "Pr\u00e9pare Requ\u00eate Claude",
"type": "main",
"index": 0
}
]
]
},
"Pr\u00e9pare Requ\u00eate Claude": {
"main": [
[
{
"node": "\ud83e\udd16 Claude \u2014 Analyser Intention",
"type": "main",
"index": 0
}
]
]
},
"\ud83e\udd16 Claude \u2014 Analyser Intention": {
"main": [
[
{
"node": "\u2699\ufe0f Parser R\u00e9ponse Claude",
"type": "main",
"index": 0
}
]
]
},
"\u2699\ufe0f Parser R\u00e9ponse Claude": {
"main": [
[
{
"node": "\ud83d\udd00 Router par Intention",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcc5 Cr\u00e9er RDV Confirm\u00e9": {
"main": [
[
{
"node": "\u2705 R\u00e9ponse Webhook",
"type": "main",
"index": 0
}
]
]
}
},
"tags": [
"mediflow",
"whatsapp",
"healthcare",
"claude-ai"
]
}
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.
anthropicApigoogleSheetsOAuth2ApihttpHeaderAuth
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
MediFlow — Prise de Rendez-vous WhatsApp (Evolution API). Uses googleSheets, httpRequest. Webhook trigger; 15 nodes.
Source: https://github.com/guilloulearnlife/guilloulearnlife/blob/faad082af35d536f7fb30b49ee497cbdd357b6eb/docs/workflows/mediflow-rdv-whatsapp.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This workflow is a complete, production-ready solution for recovering abandoned carts in Shopify stores using a multi-channel, multi-touch approach. It automates personalized follow-ups via Email, SMS
WF_UNIFIED_LEGAL_AUTOMATION. Uses googleSheets, httpRequest, telegram, telegramTrigger. Webhook trigger; 53 nodes.
This workflow captures customer messages from WhatsApp or Telegram, logs the lead in Google Sheets, and replies with a Stripe Payment Link. After Stripe confirms a successful Checkout payment, it look
01_order_processing_tilda. Uses stickyNote, googleSheets, httpRequest, telegram. Webhook trigger; 25 nodes.
Turn every sales meeting into a coaching opportunity. This workflow automatically analyzes tldv meeting recordings using OpenAI (GPT-4) to provide instant, actionable feedback to your sales team.