AutomationFlowsSlack & Telegram › Whatsapp Business Automation - Coedwig Creations

Whatsapp Business Automation - Coedwig Creations

WhatsApp Business Automation - Coedwig Creations. Uses n8nTable, httpRequest. Webhook trigger; 26 nodes.

Webhook trigger★★★★☆ complexity26 nodesN8N TableHTTP Request
Slack & Telegram Trigger: Webhook Nodes: 26 Complexity: ★★★★☆ Added:

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
{
  "name": "WhatsApp Business Automation - Coedwig Creations",
  "nodes": [
    {
      "parameters": {
        "path": "whatsapp-webhook",
        "httpMethod": "={{$request.method}}",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000001",
      "name": "WhatsApp Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        0,
        400
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "verify-check",
              "leftValue": "={{ $json.query['hub.mode'] }}",
              "rightValue": "subscribe",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000002",
      "name": "Is Verification?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        220,
        400
      ]
    },
    {
      "parameters": {
        "respondWith": "text",
        "responseBody": "={{ $json.query['hub.challenge'] }}",
        "options": {
          "responseCode": 200
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000003",
      "name": "Verify Token Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        440,
        300
      ]
    },
    {
      "parameters": {
        "respondWith": "text",
        "responseBody": "OK",
        "options": {
          "responseCode": 200
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000004",
      "name": "ACK Message",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        440,
        500
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "has-messages",
              "leftValue": "={{ $json.body?.entry?.[0]?.changes?.[0]?.value?.messages?.[0]?.text?.body }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "isNotEmpty"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000005",
      "name": "Has Text Message?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        660,
        500
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Extract WhatsApp message details\nconst entry = $input.item.json.body?.entry?.[0];\nconst change = entry?.changes?.[0]?.value;\nconst message = change?.messages?.[0];\nconst contact = change?.contacts?.[0];\n\nreturn {\n  json: {\n    message_id: message?.id || '',\n    from_phone: message?.from || '',\n    contact_name: contact?.profile?.name || 'Unknown',\n    message_text: message?.text?.body || '',\n    message_type: message?.type || 'text',\n    timestamp: message?.timestamp || '',\n    phone_number_id: change?.metadata?.phone_number_id || '',\n    display_phone: change?.metadata?.display_phone_number || '',\n    received_at: new Date().toISOString()\n  }\n};"
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000006",
      "name": "Extract Message Data",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        880,
        400
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "tableId": "wa_messages",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "message_id": "={{ $json.message_id }}",
            "from_phone": "={{ $json.from_phone }}",
            "contact_name": "={{ $json.contact_name }}",
            "message_text": "={{ $json.message_text }}",
            "message_type": "={{ $json.message_type }}",
            "timestamp": "={{ $json.timestamp }}",
            "received_at": "={{ $json.received_at }}",
            "direction": "inbound",
            "classification": "",
            "responded": "false"
          },
          "matchingColumns": [],
          "schema": []
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000007",
      "name": "Log Inbound Message",
      "type": "n8n-nodes-base.n8nTable",
      "typeVersion": 1,
      "position": [
        1100,
        300
      ]
    },
    {
      "parameters": {
        "url": "={{ $('Extract Message Data').item.json.ollama_url || 'https://single-executives-dedicated-flying.trycloudflare.com' }}/api/chat",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"gemma4:26b\",\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"You are a message classifier for a service business (Coedwig Creations - woodworking, building, renovation, repairs). Classify the incoming WhatsApp message into EXACTLY ONE category:\\n\\n1. LEAD_INQUIRY - New potential customer asking about services, pricing, availability\\n2. BOOKING_REQUEST - Customer wants to book/schedule a job\\n3. QUOTE_REQUEST - Customer asking for a quote or estimate\\n4. FOLLOW_UP - Existing customer following up on previous conversation\\n5. PAYMENT - Related to invoicing or payment\\n6. GENERAL - General conversation, greeting, or unrelated\\n7. COMPLAINT - Customer expressing dissatisfaction\\n8. REFERRAL - Someone recommending or being referred\\n\\nRespond with ONLY a JSON object: {\\\"category\\\": \\\"CATEGORY_NAME\\\", \\\"confidence\\\": 0.0-1.0, \\\"is_lead\\\": true/false, \\\"urgency\\\": \\\"low/medium/high\\\", \\\"suggested_action\\\": \\\"brief action description\\\"}\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": \"Message from {{ $('Extract Message Data').item.json.contact_name }}: {{ $('Extract Message Data').item.json.message_text }}\"\n    }\n  ],\n  \"stream\": false,\n  \"options\": {\n    \"temperature\": 0.3,\n    \"num_ctx\": 4096\n  }\n}",
        "options": {
          "timeout": 120000
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000008",
      "name": "Classify Message (Gemma4)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1100,
        500
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Parse Gemma4 classification response\nconst response = $input.item.json;\nlet classification = {};\n\ntry {\n  const content = response.message?.content || response.choices?.[0]?.message?.content || '';\n  // Try to extract JSON from the response\n  const jsonMatch = content.match(/\\{[^}]+\\}/);\n  if (jsonMatch) {\n    classification = JSON.parse(jsonMatch[0]);\n  } else {\n    classification = {\n      category: 'GENERAL',\n      confidence: 0.5,\n      is_lead: false,\n      urgency: 'low',\n      suggested_action: 'No clear classification'\n    };\n  }\n} catch (e) {\n  classification = {\n    category: 'GENERAL',\n    confidence: 0.3,\n    is_lead: false,\n    urgency: 'low',\n    suggested_action: 'Classification failed - review manually'\n  };\n}\n\nreturn {\n  json: {\n    ...classification,\n    original_message: $('Extract Message Data').item.json\n  }\n};"
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000009",
      "name": "Parse Classification",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1320,
        500
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "is-lead",
              "leftValue": "={{ $json.is_lead }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000010",
      "name": "Is Lead?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1540,
        500
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "tableId": "wa_leads",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "phone": "={{ $json.original_message.from_phone }}",
            "name": "={{ $json.original_message.contact_name }}",
            "category": "={{ $json.category }}",
            "urgency": "={{ $json.urgency }}",
            "message_preview": "={{ $json.original_message.message_text.substring(0, 200) }}",
            "suggested_action": "={{ $json.suggested_action }}",
            "confidence": "={{ $json.confidence }}",
            "status": "new",
            "created_at": "={{ new Date().toISOString() }}",
            "auto_responded": "false"
          },
          "matchingColumns": [],
          "schema": []
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000011",
      "name": "Log Lead",
      "type": "n8n-nodes-base.n8nTable",
      "typeVersion": 1,
      "position": [
        1760,
        400
      ]
    },
    {
      "parameters": {
        "url": "https://single-executives-dedicated-flying.trycloudflare.com/api/chat",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"gemma4:26b\",\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"You are the WhatsApp auto-responder for Coedwig Creations, a service business specializing in woodworking, building, renovations, and repairs based in Wales.\\n\\nCommunication style guidelines (learned from actual message history):\\n- Friendly, casual but professional tone\\n- Use 'mate' occasionally in greetings\\n- Be direct and helpful\\n- Keep messages concise for mobile reading\\n- Use emojis sparingly\\n- Always aim to move toward booking a job\\n- If asking about availability, suggest specific times\\n- For quotes, ask for details about the job (size, materials, timeline)\\n\\nIMPORTANT:\\n- This is an AUTO-REPLY. Include a note like 'I'll get back to you personally shortly' or 'Ashley will follow up with more details'\\n- Never make up prices or commit to specific dates without confirmation\\n- For urgent requests, mention you'll prioritise getting back to them\\n- Keep response under 300 characters for WhatsApp readability\\n\\nMessage category: {{ $json.category }}\\nUrgency: {{ $json.urgency }}\\nSuggested action: {{ $json.suggested_action }}\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": \"Generate an auto-reply for this message from {{ $json.original_message.contact_name }}:\\n\\n{{ $json.original_message.message_text }}\"\n    }\n  ],\n  \"stream\": false,\n  \"options\": {\n    \"temperature\": 0.7,\n    \"num_ctx\": 4096\n  }\n}",
        "options": {
          "timeout": 120000
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000012",
      "name": "Generate Auto-Reply (Gemma4)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1980,
        400
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Extract the reply text from Gemma4 response\nconst response = $input.item.json;\nconst replyText = response.message?.content || response.choices?.[0]?.message?.content || 'Thanks for your message! I\\'ll get back to you shortly.';\n\n// Clean up - remove quotes if wrapped\nlet cleanReply = replyText.trim();\nif (cleanReply.startsWith('\"') && cleanReply.endsWith('\"')) {\n  cleanReply = cleanReply.slice(1, -1);\n}\n\n// Truncate for WhatsApp\nif (cleanReply.length > 1000) {\n  cleanReply = cleanReply.substring(0, 997) + '...';\n}\n\nreturn {\n  json: {\n    reply_text: cleanReply,\n    to_phone: $('Parse Classification').item.json.original_message.from_phone,\n    phone_number_id: $('Parse Classification').item.json.original_message.phone_number_id,\n    contact_name: $('Parse Classification').item.json.original_message.contact_name,\n    category: $('Parse Classification').item.json.category\n  }\n};"
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000013",
      "name": "Prepare Reply",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2200,
        400
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://graph.facebook.com/v21.0/{{ $json.phone_number_id }}/messages",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"messaging_product\": \"whatsapp\",\n  \"recipient_type\": \"individual\",\n  \"to\": \"{{ $json.to_phone }}\",\n  \"type\": \"text\",\n  \"text\": {\n    \"preview_url\": false,\n    \"body\": {{ JSON.stringify($json.reply_text) }}\n  }\n}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000014",
      "name": "Send WhatsApp Reply",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2420,
        400
      ],
      "notes": "Uses Meta Graph API. Set up HTTP Header Auth credential with: Name=Authorization, Value=Bearer YOUR_META_API_KEY"
    },
    {
      "parameters": {
        "operation": "append",
        "tableId": "wa_messages",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "message_id": "=auto_reply_{{ Date.now() }}",
            "from_phone": "=business",
            "contact_name": "=Coedwig Creations",
            "message_text": "={{ $('Prepare Reply').item.json.reply_text }}",
            "message_type": "auto_reply",
            "timestamp": "={{ Math.floor(Date.now() / 1000) }}",
            "received_at": "={{ new Date().toISOString() }}",
            "direction": "outbound",
            "classification": "={{ $('Parse Classification').item.json.category }}",
            "responded": "true"
          },
          "matchingColumns": [],
          "schema": []
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000015",
      "name": "Log Outbound Reply",
      "type": "n8n-nodes-base.n8nTable",
      "typeVersion": 1,
      "position": [
        2640,
        400
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 2
            }
          ]
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000020",
      "name": "Every 2 Hours",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        900
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=https://graph.facebook.com/v21.0/961196760109668/conversations?fields=id,link,message_count,name,updated_time,can_reply,senders&access_token={{ encodeURIComponent('EAAU8x8cnnZCMBRCI95NAWlau8DirfJff2WtCPTyve8jH0zg8AEFCy3K3PpZBACYC7uhQtW8XrXgLhi42ZCaaYdJKBgZB8vs3C76ruL82fqB7UGbIPOVTo9oXAGxIWjW00w4SIDRK8We2vBG6TbJIq7ZCUXZA98oXXJZA8fLS6UDdMy07vhVJjgp0o9i8YubqcZBFzYae7r9eEAi0gwZDZD') }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000021",
      "name": "Fetch WhatsApp Conversations",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        220,
        800
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=https://graph.facebook.com/v21.0/act_{{ '1474203620974579' }}/insights?fields=campaign_name,impressions,clicks,spend,actions,cost_per_action_type,ctr,cpc&date_preset=last_7d&level=campaign&access_token={{ encodeURIComponent('EAAU8x8cnnZCMBRCI95NAWlau8DirfJff2WtCPTyve8jH0zg8AEFCy3K3PpZBACYC7uhQtW8XrXgLhi42ZCaaYdJKBgZB8vs3C76ruL82fqB7UGbIPOVTo9oXAGxIWjW00w4SIDRK8We2vBG6TbJIq7ZCUXZA98oXXJZA8fLS6UDdMy07vhVJjgp0o9i8YubqcZBFzYae7r9eEAi0gwZDZD') }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000022",
      "name": "Fetch Ad Performance",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        220,
        1000
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=https://graph.facebook.com/v21.0/961196760109668?fields=analytics.start({{ $now.minus({days: 7}).toFormat('yyyy-MM-dd') }}).end({{ $now.toFormat('yyyy-MM-dd') }}).granularity(DAY).phone_numbers([])&access_token={{ encodeURIComponent('EAAU8x8cnnZCMBRCI95NAWlau8DirfJff2WtCPTyve8jH0zg8AEFCy3K3PpZBACYC7uhQtW8XrXgLhi42ZCaaYdJKBgZB8vs3C76ruL82fqB7UGbIPOVTo9oXAGxIWjW00w4SIDRK8We2vBG6TbJIq7ZCUXZA98oXXJZA8fLS6UDdMy07vhVJjgp0o9i8YubqcZBFzYae7r9eEAi0gwZDZD') }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000023",
      "name": "Fetch WhatsApp Analytics",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        220,
        1200
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Merge all analytics data for the report\nconst conversations = $('Fetch WhatsApp Conversations').first()?.json || {};\nconst adPerformance = $('Fetch Ad Performance').first()?.json || {};\nconst waAnalytics = $('Fetch WhatsApp Analytics').first()?.json || {};\n\nreturn {\n  json: {\n    conversations: JSON.stringify(conversations).substring(0, 3000),\n    ad_performance: JSON.stringify(adPerformance).substring(0, 3000),\n    wa_analytics: JSON.stringify(waAnalytics).substring(0, 3000),\n    report_date: new Date().toISOString()\n  }\n};"
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000024",
      "name": "Merge Analytics Data",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        500,
        1000
      ]
    },
    {
      "parameters": {
        "url": "https://single-executives-dedicated-flying.trycloudflare.com/api/chat",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"gemma4:26b\",\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"You are a business analytics AI for Coedwig Creations, a service business (woodworking, building, renovations, repairs) in Wales.\\n\\nGenerate a concise business intelligence report covering:\\n1. LEAD ANALYSIS - New leads, conversion potential, follow-up priorities\\n2. AD PERFORMANCE - Which campaigns are working, cost per lead, ROI insights\\n3. CONVERSATION INSIGHTS - Response times, message patterns, customer sentiment\\n4. ACTION ITEMS - Top 5 specific actions to take right now\\n5. CONVERSION TIPS - Based on the data, how to convert more leads to paying clients\\n\\nFormat as a clean report with headers and bullet points. Be specific with numbers where available. Focus on actionable insights, not generic advice.\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": \"Generate a 2-hourly business report based on this data:\\n\\nWhatsApp Conversations: {{ $json.conversations }}\\n\\nAd Performance: {{ $json.ad_performance }}\\n\\nWhatsApp Analytics: {{ $json.wa_analytics }}\\n\\nReport time: {{ $json.report_date }}\"\n    }\n  ],\n  \"stream\": false,\n  \"options\": {\n    \"temperature\": 0.5,\n    \"num_ctx\": 8192\n  }\n}",
        "options": {
          "timeout": 180000
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000025",
      "name": "Generate Report (Gemma4)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        720,
        1000
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "tableId": "wa_reports",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "report_date": "={{ $('Merge Analytics Data').item.json.report_date }}",
            "report_content": "={{ $json.message?.content || 'Report generation failed' }}",
            "report_type": "bi_report_2h",
            "ad_data_snapshot": "={{ $('Merge Analytics Data').item.json.ad_performance.substring(0, 500) }}",
            "wa_data_snapshot": "={{ $('Merge Analytics Data').item.json.wa_analytics.substring(0, 500) }}"
          },
          "matchingColumns": [],
          "schema": []
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000026",
      "name": "Store Report",
      "type": "n8n-nodes-base.n8nTable",
      "typeVersion": 1,
      "position": [
        940,
        1000
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=https://graph.facebook.com/v21.0/961196760109668/message_templates?fields=name,status,language,category&access_token={{ encodeURIComponent('EAAU8x8cnnZCMBRCI95NAWlau8DirfJff2WtCPTyve8jH0zg8AEFCy3K3PpZBACYC7uhQtW8XrXgLhi42ZCaaYdJKBgZB8vs3C76ruL82fqB7UGbIPOVTo9oXAGxIWjW00w4SIDRK8We2vBG6TbJIq7ZCUXZA98oXXJZA8fLS6UDdMy07vhVJjgp0o9i8YubqcZBFzYae7r9eEAi0gwZDZD') }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000030",
      "name": "Fetch Message Templates",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        220,
        1500
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Check for leads that need outreach\n// This would check the wa_leads table for leads marked as 'new' that haven't been contacted\n// For now, we build the outreach message structure\n\nconst templates = $input.item.json;\nconst availableTemplates = templates.data || [];\n\n// Find a suitable template for lead outreach\nlet outreachTemplate = availableTemplates.find(t => \n  t.status === 'APPROVED' && \n  (t.category === 'MARKETING' || t.category === 'UTILITY')\n);\n\nreturn {\n  json: {\n    has_templates: availableTemplates.length > 0,\n    template_count: availableTemplates.length,\n    outreach_template: outreachTemplate || null,\n    templates_summary: availableTemplates.map(t => ({\n      name: t.name,\n      status: t.status,\n      category: t.category\n    }))\n  }\n};"
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000031",
      "name": "Check Outreach Templates",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        1500
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "tableId": "wa_outreach_log",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "run_timestamp": "={{ new Date().toISOString() }}",
            "templates_available": "={{ $json.template_count }}",
            "outreach_template_used": "={{ $json.outreach_template?.name || 'none' }}",
            "status": "checked"
          },
          "matchingColumns": [],
          "schema": []
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000032",
      "name": "Log Outreach Run",
      "type": "n8n-nodes-base.n8nTable",
      "typeVersion": 1,
      "position": [
        660,
        1500
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "tableId": "wa_messages",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "message_id": "=non_lead_{{ Date.now() }}",
            "from_phone": "={{ $('Extract Message Data').item.json.from_phone }}",
            "contact_name": "={{ $('Extract Message Data').item.json.contact_name }}",
            "message_text": "={{ $('Extract Message Data').item.json.message_text }}",
            "message_type": "={{ $('Extract Message Data').item.json.message_type }}",
            "timestamp": "={{ $('Extract Message Data').item.json.timestamp }}",
            "received_at": "={{ $('Extract Message Data').item.json.received_at }}",
            "direction": "inbound",
            "classification": "={{ $json.category }}",
            "responded": "false"
          },
          "matchingColumns": [],
          "schema": []
        }
      },
      "id": "a1b2c3d4-0001-4000-8000-000000000040",
      "name": "Log Non-Lead Message",
      "type": "n8n-nodes-base.n8nTable",
      "typeVersion": 1,
      "position": [
        1760,
        600
      ]
    }
  ],
  "connections": {
    "WhatsApp Webhook": {
      "main": [
        [
          {
            "node": "Is Verification?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Verification?": {
      "main": [
        [
          {
            "node": "Verify Token Response",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "ACK Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ACK Message": {
      "main": [
        [
          {
            "node": "Has Text Message?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Text Message?": {
      "main": [
        [
          {
            "node": "Extract Message Data",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Extract Message Data": {
      "main": [
        [
          {
            "node": "Log Inbound Message",
            "type": "main",
            "index": 0
          },
          {
            "node": "Classify Message (Gemma4)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Message (Gemma4)": {
      "main": [
        [
          {
            "node": "Parse Classification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Classification": {
      "main": [
        [
          {
            "node": "Is Lead?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Lead?": {
      "main": [
        [
          {
            "node": "Log Lead",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Non-Lead Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Lead": {
      "main": [
        [
          {
            "node": "Generate Auto-Reply (Gemma4)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Auto-Reply (Gemma4)": {
      "main": [
        [
          {
            "node": "Prepare Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Reply": {
      "main": [
        [
          {
            "node": "Send WhatsApp Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send WhatsApp Reply": {
      "main": [
        [
          {
            "node": "Log Outbound Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Every 2 Hours": {
      "main": [
        [
          {
            "node": "Fetch WhatsApp Conversations",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Ad Performance",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch WhatsApp Analytics",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Message Templates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch WhatsApp Conversations": {
      "main": [
        [
          {
            "node": "Merge Analytics Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Ad Performance": {
      "main": [
        [
          {
            "node": "Merge Analytics Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch WhatsApp Analytics": {
      "main": [
        [
          {
            "node": "Merge Analytics Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Analytics Data": {
      "main": [
        [
          {
            "node": "Generate Report (Gemma4)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Report (Gemma4)": {
      "main": [
        [
          {
            "node": "Store Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Message Templates": {
      "main": [
        [
          {
            "node": "Check Outreach Templates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Outreach Templates": {
      "main": [
        [
          {
            "node": "Log Outreach Run",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true,
    "callerPolicy": "workflowsFromSameOwner",
    "errorWorkflow": ""
  },
  "staticData": null,
  "tags": [],
  "triggerCount": 2
}
Pro

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

About this workflow

WhatsApp Business Automation - Coedwig Creations. Uses n8nTable, httpRequest. Webhook trigger; 26 nodes.

Source: https://gist.github.com/Turkey-Dinosaur/7ee83f5219b222716b72946ead2cee7c — 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

HR teams, IT Operations, and System Administrators managing employee onboarding at scale. It’s perfect if you use Odoo 18 to trigger account requests and need Redmine + GitLab accounts created instant

HTTP Request, Slack
Slack & Telegram

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

HTTP Request, Shopify, SendGrid +5
Slack & Telegram

qualiopi. Uses airtable, telegram, emailSend, httpRequest. Webhook trigger; 51 nodes.

Airtable, Telegram, Email Send +3
Slack & Telegram

This workflow automates end-to-end research analysis by coordinating multiple AI models—including NVIDIA NIM (Llama), OpenAI GPT-4, and Claude to analyze uploaded documents, extract insights, and gene

HTTP Request, Postgres, Slack +1
Slack & Telegram

PsyCardv2. Uses executeCommand, telegram, readBinaryFile, googleDrive. Webhook trigger; 41 nodes.

Execute Command, Telegram, Read Binary File +2