{
  "name": "Flag Slowing Zoho CRM Deals with n8n (free workflow JSON)",
  "nodes": [
    {
      "id": "2630470b-a369-4bc2-b722-d8ffcb04ef62",
      "name": "Monitor Deal Activity",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1056,
        336
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 2
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "38444d6c-ceb7-49cb-acc5-8246afd172fa",
      "name": "Get Active Deals",
      "type": "n8n-nodes-base.zohoCrm",
      "position": [
        1296,
        336
      ],
      "parameters": {
        "limit": 100,
        "options": {},
        "resource": "deal",
        "operation": "getAll"
      },
      "typeVersion": 1
    },
    {
      "id": "f308510e-f84d-446c-974c-e551822d3c0e",
      "name": "Process Each Deal",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        1536,
        336
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "data"
      },
      "typeVersion": 1
    },
    {
      "id": "e9920b69-911e-4cb2-a613-64aed8ee00b2",
      "name": "Calculate Momentum Metrics",
      "type": "n8n-nodes-base.code",
      "position": [
        1872,
        336
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Calculate engagement velocity, momentum score, and activity patterns\nconst deal = $input.item.json;\n\n// Extract deal basic info\nconst dealId = deal.id || deal.Deal_ID || 'unknown';\nconst dealName = deal.Deal_Name || deal.name || 'Unnamed Deal';\nconst dealValue = deal.Amount || deal.value || 0;\n\n// Get activities (assuming they're in the deal data)\nconst activities = deal.Activities || deal.activities || [];\n\n// Calculate date ranges\nconst now = new Date();\nconst thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);\nconst fifteenDaysAgo = new Date(now.getTime() - 15 * 24 * 60 * 60 * 1000);\n\n// Filter activities by time period\nconst last30DaysActivities = activities.filter(activity => {\n  const activityDate = new Date(activity.date || activity.Created_Time);\n  return activityDate >= thirtyDaysAgo && activityDate <= now;\n});\n\nconst last15DaysActivities = activities.filter(activity => {\n  const activityDate = new Date(activity.date || activity.Created_Time);\n  return activityDate >= fifteenDaysAgo && activityDate <= now;\n});\n\nconst first15DaysActivities = activities.filter(activity => {\n  const activityDate = new Date(activity.date || activity.Created_Time);\n  return activityDate >= thirtyDaysAgo && activityDate < fifteenDaysAgo;\n});\n\n// Calculate velocity (activities per week)\nconst currentVelocity = (last15DaysActivities.length / 15) * 7; // Convert to per week\nconst previousVelocity = (first15DaysActivities.length / 15) * 7; // Convert to per week\nconst averageVelocity = (last30DaysActivities.length / 30) * 7;\n\n// Calculate activity frequency (activities per day over 30 days)\nconst activityFrequency = last30DaysActivities.length / 30;\n\n// Calculate days since last activity\nlet daysSinceLastActivity = null;\nif (activities.length > 0) {\n  const sortedActivities = activities.sort((a, b) => {\n    const dateA = new Date(a.date || a.Created_Time);\n    const dateB = new Date(b.date || b.Created_Time);\n    return dateB - dateA;\n  });\n  const lastActivityDate = new Date(sortedActivities[0].date || sortedActivities[0].Created_Time);\n  daysSinceLastActivity = Math.floor((now - lastActivityDate) / (24 * 60 * 60 * 1000));\n} else {\n  daysSinceLastActivity = 999; // No activities found\n}\n\n// Calculate momentum score (0-100)\nlet momentumScore = 0;\n\n// Factor 1: Current velocity (40 points max)\nconst velocityScore = Math.min((currentVelocity / 5) * 40, 40); // 5 activities/week = max\n\n// Factor 2: Velocity trend (30 points max)\nconst velocityChange = currentVelocity - previousVelocity;\nconst trendScore = velocityChange > 0 ? Math.min((velocityChange / 2) * 30, 30) : 0;\n\n// Factor 3: Recency (30 points max)\nconst recencyScore = daysSinceLastActivity <= 3 ? 30 : \n                     daysSinceLastActivity <= 7 ? 20 : \n                     daysSinceLastActivity <= 14 ? 10 : 0;\n\nmomentumScore = Math.round(velocityScore + trendScore + recencyScore);\n\n// Determine momentum trend\nlet momentumTrend = 'stable';\nconst velocityChangePercent = previousVelocity > 0 ? \n  ((currentVelocity - previousVelocity) / previousVelocity) * 100 : 0;\n\nif (velocityChangePercent > 20) {\n  momentumTrend = 'increasing';\n} else if (velocityChangePercent < -20) {\n  momentumTrend = 'decreasing';\n}\n\n// Determine if deal is slowing\nconst isSlowing = currentVelocity < previousVelocity && \n                  currentVelocity < averageVelocity * 0.7;\n\n// Return the calculated metrics\nreturn {\n  dealId: dealId,\n  dealName: dealName,\n  dealValue: dealValue,\n  currentVelocity: Math.round(currentVelocity * 100) / 100,\n  previousVelocity: Math.round(previousVelocity * 100) / 100,\n  momentumScore: momentumScore,\n  daysSinceLastActivity: daysSinceLastActivity,\n  activityFrequency: Math.round(activityFrequency * 100) / 100,\n  momentumTrend: momentumTrend,\n  isSlowing: isSlowing,\n  totalActivitiesLast30Days: last30DaysActivities.length,\n  velocityChangePercent: Math.round(velocityChangePercent * 100) / 100\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "78abb92a-cb91-44e5-9c97-4b74f6807639",
      "name": "Filter Slowing Deals",
      "type": "n8n-nodes-base.if",
      "position": [
        2112,
        464
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $('Calculate Momentum Metrics').item.json.isSlowing }}"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "1859798e-fbfa-47db-8b35-d24ee517bdc9",
      "name": "Generate Acceleration Tactics",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        2384,
        336
      ],
      "parameters": {
        "text": "=={{\n  JSON.stringify({\n    deal: $json\n  })\n}}",
        "options": {
          "systemMessage": "You are a sales acceleration expert.\n\nAnalyze the deal data and return ONLY valid JSON (no markdown, no explanation).\n\nFormat:\n{\n  \"tactics\": [\n    {\n      \"title\": \"short title\",\n      \"action\": \"what to do\",\n      \"next_step\": \"specific immediate step\"\n    }\n  ],\n  \"priority\": \"high | medium | low\",\n  \"reason\": \"why this deal is slowing\"\n}"
        },
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "c391d5b3-fda9-41d8-99fc-60b74e64cf99",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        2400,
        528
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "id",
          "value": "gpt-4o-mini"
        },
        "options": {},
        "builtInTools": {}
      },
      "typeVersion": 1.3
    },
    {
      "id": "1a1f161d-2a5d-4010-ba88-7a614519be2e",
      "name": "Prepare Alert Message",
      "type": "n8n-nodes-base.set",
      "position": [
        3616,
        432
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "dealName",
              "type": "string",
              "value": "={{ $json.dealName }}"
            },
            {
              "id": "id-7",
              "name": "alertMessage",
              "type": "string",
              "value": "={{ $json.alertMessage }}"
            },
            {
              "id": "45c2e476-c848-4b91-af8e-b297a57ba61c",
              "name": "dealId",
              "type": "string",
              "value": "={{ $json.dealId }}"
            },
            {
              "id": "561dc087-e438-489b-850d-2726ccf98a32",
              "name": "priority",
              "type": "string",
              "value": "={{ $json.priority }}"
            },
            {
              "id": "b78b23ef-0968-4bd8-a564-55812773b7b8",
              "name": "reason",
              "type": "string",
              "value": "={{ $json.reason }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "1361bd5a-1c82-44ca-b4a0-83d7fd32d1a5",
      "name": "Send Alert to Sales Team",
      "type": "n8n-nodes-base.slack",
      "position": [
        3872,
        432
      ],
      "parameters": {
        "text": "={{ $json.alertMessage }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "otherOptions": {}
      },
      "typeVersion": 2.4
    },
    {
      "id": "44b033f9-f76c-4a5f-a7b2-62d5c5cc2721",
      "name": "Enrich Deals with AI",
      "type": "n8n-nodes-base.merge",
      "position": [
        2944,
        432
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineByPosition"
      },
      "typeVersion": 3.2
    },
    {
      "id": "4eddb617-6369-4c13-a7a0-456b46f9a383",
      "name": "Format Deal Alert Message",
      "type": "n8n-nodes-base.code",
      "position": [
        3232,
        432
      ],
      "parameters": {
        "jsCode": "return items.map(item => {\n  \n  // Step 1: Parse AI output\n  let parsed;\n\n  if (typeof item.json.output === \"string\") {\n    try {\n      parsed = JSON.parse(item.json.output);\n    } catch (e) {\n      throw new Error(\"Invalid JSON from AI: \" + item.json.output);\n    }\n  } else {\n    parsed = item.json.output;\n  }\n\n  // Step 2: Build tactics text\n  let tacticsText = \"\";\n\n  parsed.tactics.forEach((t, index) => {\n    tacticsText += `\\n${index + 1}. ${t.title}\\n- ${t.action}\\n- Next: ${t.next_step}\\n`;\n  });\n\n  // Step 3: Return one item per input\n  return {\n    json: {\n      dealId: item.json.dealId,\n      dealName: item.json.dealName,\n      priority: parsed.priority,\n      reason: parsed.reason,\n      alertMessage: `Deal Momentum Alert: ${item.json.dealName}\n\nDeal Value: $${item.json.dealValue}\nMomentum Score: ${item.json.momentumScore}/100\nDays Since Last Activity: ${item.json.daysSinceLastActivity}\nTrend: ${item.json.momentumTrend}\nPriority: ${parsed.priority.toUpperCase()}\n\nRecommended Tactics:\n${tacticsText}\n\nReason: ${parsed.reason}`\n    }\n  };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "6edf654c-82a2-40b0-88db-f918ac2eb2b8",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        992,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 736,
        "height": 480,
        "content": "## Fetch and Prepare Deal Data\nThis section triggers the workflow on a schedule, retrieves active deals from the CRM, and splits them into individual records. It ensures each deal is processed independently, enabling accurate analysis and downstream automation for every opportunity."
      },
      "typeVersion": 1
    },
    {
      "id": "55148013-fed4-43cd-a444-c1c47e7615e4",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1776,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 976,
        "height": 480,
        "content": "## Analyze Deal Momentum and Identify Risks\nThis section calculates key performance metrics like velocity, activity frequency, and momentum score. It filters out slowing deals and uses AI to generate actionable recommendations, helping identify at-risk opportunities that need immediate attention."
      },
      "typeVersion": 1
    },
    {
      "id": "d2774b52-9f23-4986-b6ea-a39596c61084",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2800,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 480,
        "content": "## Combine AI Insights and Format Alerts\nThis section merges AI-generated recommendations with deal data and formats them into a structured alert message. It transforms raw insights into a clear, readable format that sales teams can quickly understand and act upon."
      },
      "typeVersion": 1
    },
    {
      "id": "be1e3758-faaa-43f7-9f68-4c01b26a4137",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3488,
        160
      ],
      "parameters": {
        "color": 7,
        "width": 592,
        "height": 480,
        "content": "## Prepare and Send Notifications\nThis section prepares the final alert payload and sends it to the sales team via Slack. It ensures timely delivery of actionable insights so the team can respond quickly and re-engage deals that are losing momentum."
      },
      "typeVersion": 1
    },
    {
      "id": "bd626727-ce06-4a8f-897e-71887ea96878",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        192,
        -208
      ],
      "parameters": {
        "width": 752,
        "height": 1500,
        "content": "## How Workflow Works\n\n- The workflow starts on a scheduled interval (every 2 hours) to continuously monitor deal activity without manual effort.\n- It fetches up to 100 deals from Zoho CRM (this file sets no stage or status filter) and splits them into individual items so each deal can be processed independently.\n- For every deal, momentum metrics are calculated using recent activity data, including velocity, frequency, and recency.\n- Deals showing signs of slowdown are filtered based on declining velocity and low engagement signals.\n- These filtered deals are sent to an AI model, which analyzes the situation and generates recommended tactics, priority level, and reasoning.\n- The AI output is merged back with the original deal data to maintain full context.\n- A formatted alert message is created, combining deal insights and AI recommendations into a readable structure.\n- Finally, the alert is sent to the sales team via Slack, enabling quick action on at-risk deals.\n\n\n\n## Setup Steps\n\n- Connect your Zoho CRM account in n8n and configure the Get Active Deals node to fetch deal data, including activities.\n- Set up the Schedule Trigger node to define how frequently the workflow should run (e.g., every 2 hours).\n- Ensure your deal data includes activity history (calls, emails, meetings) required for momentum calculation.\n- Configure the AI node with an OpenAI (or similar) API key and set the prompt to return structured JSON output.\n- Use a Merge node in “Combine by Position” mode to correctly align AI output with corresponding deals.\n- Add the Code node to parse AI output and format the alert message properly.\n- Connect your Slack account and select the appropriate channel in the Send Alert node.\n- Test the workflow with sample data to verify outputs, then activate it for continuous automated monitoring.\n\n## Before you turn it on\n\n1. The momentum calculation reads a list called Activities inside each deal. This file does not ask Zoho for activities. If the list is missing, every deal shows no activity and nothing is ever flagged. Check the output of Get Active Deals first.\n2. There is no stage filter, so closed deals can be analyzed too, and only the first 100 deals are read.\n3. Nothing remembers past alerts. A slowing deal can be reported again at every run (every 2 hours).\n4. The AI sees only the calculated numbers, the deal name and the amount, not the stage, notes or contacts.\n5. Select a Slack channel you can safely post to before the first run, and run it by hand first.\n6. Deal names and amounts are sent to OpenAI and to Slack. Check your own rules for customer data.\n\nSource: based on a template from the n8n Library at n8n.io, documented by LeenLabs.\nNot tested on live APIs."
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Get Active Deals": {
      "main": [
        [
          {
            "node": "Process Each Deal",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Generate Acceleration Tactics",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Process Each Deal": {
      "main": [
        [
          {
            "node": "Calculate Momentum Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Enrich Deals with AI": {
      "main": [
        [
          {
            "node": "Format Deal Alert Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Slowing Deals": {
      "main": [
        [
          {
            "node": "Generate Acceleration Tactics",
            "type": "main",
            "index": 0
          },
          {
            "node": "Enrich Deals with AI",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Monitor Deal Activity": {
      "main": [
        [
          {
            "node": "Get Active Deals",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Alert Message": {
      "main": [
        [
          {
            "node": "Send Alert to Sales Team",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Deal Alert Message": {
      "main": [
        [
          {
            "node": "Prepare Alert Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Momentum Metrics": {
      "main": [
        [
          {
            "node": "Filter Slowing Deals",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Acceleration Tactics": {
      "main": [
        [
          {
            "node": "Enrich Deals with AI",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  }
}