{
  "id": "OhJsiEh8K8qLNiZP",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Sales Rep Behavioral Pattern Analyzer",
  "tags": [],
  "nodes": [
    {
      "id": "9c1fcafe-7569-47a2-88bb-2a0a93978173",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        896,
        1168
      ],
      "parameters": {
        "color": 7,
        "width": 1296,
        "height": 560,
        "content": "## Salesforce Data Collection.\nFetch recent Opportunities from Salesforce and build a reusable list of Opportunity IDs. This creates the core dataset used to retrieve all related sales activities downstream."
      },
      "typeVersion": 1
    },
    {
      "id": "0bd47937-d2e3-487f-af93-b6c9cdaa5b85",
      "name": "Start Workflow",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        944,
        1424
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "5eefa097-894e-451d-bc5c-94504dacefb3",
      "name": "Get Opportunity Metadata",
      "type": "n8n-nodes-base.salesforce",
      "position": [
        1456,
        1424
      ],
      "parameters": {
        "resource": "opportunity",
        "operation": "get",
        "opportunityId": "="
      },
      "typeVersion": 1
    },
    {
      "id": "71b780e7-3f50-4cd6-a045-4327fdd95d28",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2448,
        992
      ],
      "parameters": {
        "color": 7,
        "width": 1280,
        "height": 800,
        "content": "## Activity Enrichment (Tasks + Events)\nRetrieve Tasks and Events linked to the selected Opportunities and merge them into a unified activity stream, creating a complete view of sales rep engagement per deal."
      },
      "typeVersion": 1
    },
    {
      "id": "7a1cb9bd-c5cd-41d5-99c6-f01641fae639",
      "name": "Fetch Tasks for Opportunities",
      "type": "n8n-nodes-base.salesforce",
      "position": [
        2640,
        1200
      ],
      "parameters": {
        "query": "=SELECT     Id,     OwnerId,     WhatId,     Subject,     ActivityDate,     CreatedDate FROM Task WHERE WhatId IN ({{$json.opportunityIdsString}})",
        "resource": "search"
      },
      "typeVersion": 1
    },
    {
      "id": "cc2852c8-7ada-44f8-ba3a-5914d8498500",
      "name": "Merge Tasks + Events",
      "type": "n8n-nodes-base.merge",
      "position": [
        3072,
        1440
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "de2155bb-9f3e-4446-be2b-ca1045396dde",
      "name": "Merge Opportunities + Activities",
      "type": "n8n-nodes-base.merge",
      "position": [
        3472,
        1328
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "0b39a9d7-d248-4462-8bef-6efa49751c54",
      "name": "Fetch Events for Opportunities",
      "type": "n8n-nodes-base.salesforce",
      "position": [
        2640,
        1552
      ],
      "parameters": {
        "query": "=SELECT\n    Id,\n    OwnerId,\n    WhatId,\n    Subject,\n    ActivityDate,\n    CreatedDate\nFROM Event\nWHERE WhatId IN ({{$json.opportunityIdsString}})",
        "resource": "search"
      },
      "typeVersion": 1
    },
    {
      "id": "426785cb-e770-4de2-9872-ef5d29e8b47e",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4032,
        1088
      ],
      "parameters": {
        "color": 7,
        "width": 928,
        "height": 560,
        "content": "## Sales Activity Intelligence Engine \nTransform raw Salesforce records into behavioral metrics by calculating touches, calls, emails, meetings, stale deal gaps, and rep-level performance indicators."
      },
      "typeVersion": 1
    },
    {
      "id": "e475666c-07e9-4c7a-8fe6-2e94927d5019",
      "name": "Compute Opportunity Activity Metrics",
      "type": "n8n-nodes-base.code",
      "position": [
        4192,
        1328
      ],
      "parameters": {
        "jsCode": "const records = $input.all().map(i => i.json);\n\n// split data\nconst opportunities = records.filter(r => r.StageName);\nconst activities = records.filter(r => r.Subject);\n\n// helper classifier\nfunction classifyActivity(subject = \"\") {\n  const s = subject.toLowerCase();\n\n  if (s.includes(\"call\") || s.includes(\"negotiation\") || s.includes(\"discovery\")|| s.includes(\"check-in\") ) return \"call\";\n  \n  if (s.includes(\"email\") || s.includes(\"outreach\")) return \"email\";\n  \n  if (s.includes(\"follow\") || s.includes(\"follow-up\") )return \"followup\";\n\n  if (s.includes(\"demo\") || s.includes(\"meeting\") || s.includes(\"review\")) return \"meeting\";\n  return \"other\";\n}\n\nconst result = opportunities.map(opp => {\n  const oppActivities = activities.filter(a => a.WhatId === opp.Id);\n\n  let calls = 0;\n  let emails = 0;\n  let followups = 0;\n  let meetings = 0;\n\n  oppActivities.forEach(act => {\n    const type = classifyActivity(act.Subject);\n    if (type === \"call\") calls++;\n    if (type === \"email\") emails++;\n    if (type === \"followup\") followups++;\n    if (type === \"meeting\") meetings++;\n  });\n\n  const touches = oppActivities.length;\n\n  const lastDate = oppActivities\n    .map(a => a.ActivityDate)\n    .filter(Boolean)\n    .sort()\n    .pop();\n\n  let gap = \"No activity\";\n  if (lastDate) {\n    const diff = (new Date() - new Date(lastDate)) / (1000 * 60 * 60 * 24);\n    gap = Math.floor(diff);\n  }\n\n  return { \n    opportunity: opp.Name,\n    rep: opp.OwnerId,\n    stage: opp.StageName,\n    amount: opp.Amount,\n    touches,\n    calls,\n    emails,\n    followups,\n    meetings,\n    lastActivityDate: lastDate || null,\n    lastActivityGapDays: gap\n  };\n});\n\nreturn result.map(r => ({ json: r }));"
      },
      "typeVersion": 2
    },
    {
      "id": "f4cf11ca-f2e4-49aa-afc7-5d8c8a8c0c58",
      "name": "Aggregate Metrics Per Sales Rep",
      "type": "n8n-nodes-base.code",
      "position": [
        4608,
        1328
      ],
      "parameters": {
        "jsCode": "const deals = $input.all().map(i => i.json);\nconst reps = {};\n\nfor (const d of deals) {\n  const rep = d.rep || \"Unknown Rep\";\n\n  if (!reps[rep]) {\n    reps[rep] = {\n      rep,\n      total_deals: 0,\n      total_revenue: 0,\n      won_deals: 0,\n      lost_deals: 0,\n      calls: 0,\n      emails: 0,\n      meetings: 0,\n      followups: 0,\n      stale_deals: 0\n    };\n  }\n\n  const r = reps[rep];\n\n  r.total_deals++;\n  r.total_revenue += d.amount;\n  r.calls += d.calls;\n  r.emails += d.emails;\n  r.meetings += d.meetings;\n  r.followups += d.followups;\n\n  if (d.stage === \"Closed Won\") r.won_deals++;\n  if (d.stage === \"Closed Lost\") r.lost_deals++;\n  if (d.lastActivityGapDays > 7) r.stale_deals++;\n}\n\nreturn Object.values(reps).map(r => ({ json: r }));"
      },
      "typeVersion": 2
    },
    {
      "id": "7cff6ea6-461b-46d4-94a7-5584048e8497",
      "name": "Generate Performance Chart URL",
      "type": "n8n-nodes-base.code",
      "position": [
        5456,
        1120
      ],
      "parameters": {
        "jsCode": "// Collect rep rows\nconst reps = $input.all().map(i => i.json);\n\n// Build arrays\nconst labels = [$('Get user').first().json.recentItems[0].Name];\nconst calls = reps.map(r => r.calls);\nconst emails = reps.map(r => r.emails);\nconst meetings = reps.map(r => r.meetings);\nconst wonDeals = reps.map(r => r.won_deals);\nconst lostDeals = reps.map(r => r.lost_deals);\nconst staleDeals = reps.map(r => r.stale_deals);\n\n// Build chart config\nconst chartConfig = {\n  type: \"bar\",\n  data: {\n    labels,\n    datasets: [\n      { label: \"Calls\", data: calls, backgroundColor: \"rgba(54,162,235,0.7)\" },\n      { label: \"Emails\", data: emails, backgroundColor: \"rgba(255,99,132,0.7)\" },\n      { label: \"Meetings\", data: meetings, backgroundColor: \"rgba(255,206,86,0.7)\" },\n      { label: \"Won Deals\", data: wonDeals, backgroundColor: \"rgba(75,192,192,0.7)\" },\n      { label: \"Lost Deals\", data: lostDeals, backgroundColor: \"rgba(255,159,64,0.7)\" },\n      { label: \"Stale Deals\", data: staleDeals, backgroundColor: \"rgba(153,102,255,0.7)\" }\n    ]\n  },\n  options: {\n    plugins: {\n      title: {\n        display: true,\n        text: \"Weekly Rep Activity vs Outcomes\"\n      },\n      legend: { position: \"bottom\" }\n    },\n    scales: { y: { beginAtZero: true } }\n  }\n};\n\n// Create QuickChart URL\nconst chartUrl =\n  \"https://quickchart.io/chart?c=\" +\n  encodeURIComponent(JSON.stringify(chartConfig));\n\n// Return everything for Gmail node\nreturn [{\n  json: {\n    labels:[$('Get user').first().json.recentItems[0].Name],\n    calls,\n    emails,\n    meetings,\n    wonDeals,\n    lostDeals,\n    staleDeals,\n    chartUrl\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "a3ccbab0-2baf-465f-9fba-e1886f1487b8",
      "name": "LLM Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGroq",
      "position": [
        5392,
        1632
      ],
      "parameters": {
        "model": "openai/gpt-oss-120b",
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "15c3b945-0943-48dc-9d60-f4849a2271c5",
      "name": "AI Output Schema",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        5648,
        1648
      ],
      "parameters": {
        "schemaType": "manual",
        "inputSchema": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"performance_summary\": {\n      \"type\": \"string\",\n      \"description\": \"Overall weekly behaviour analysis of the sales rep\"\n    },\n    \"strengths\": {\n      \"type\": \"string\",\n      \"description\": \"Key strengths in activity and performance\"\n    },\n    \"risks\": {\n      \"type\": \"string\",\n      \"description\": \"Behaviour risks affecting deal outcomes\"\n    },\n    \"coaching_advice\": {\n      \"type\": \"string\",\n      \"description\": \"Coaching feedback for the sales rep\"\n    },\n    \"next_week_actions\": {\n      \"type\": \"string\",\n      \"description\": \"Concrete actions for next week\"\n    }\n  },\n  \"required\": [\n    \"performance_summary\",\n    \"strengths\",\n    \"risks\",\n    \"coaching_advice\",\n    \"next_week_actions\"\n  ]\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "ead2dd38-79ee-444e-877f-98946ac47d46",
      "name": "Generate AI Coaching Insights",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        5440,
        1424
      ],
      "parameters": {
        "text": "=Role: You are a Sales Behavioral Scientist and Performance Coach.\n\nTask: Analyze the following weekly sales rep activity data. Focus on the relationship between effort (Activities) and conversion (Outcomes).\n\nData Input: {{$json}}\n\nAnalysis Requirements:\n\nThe Outreach-to-Engagement Gap: Compare Calls/Emails to Meetings. If the ratio is low, identify if the behavior is \"Low-Quality Prospecting.\"\n\nThe \"Neglect\" Index: Analyze Stale Deals in relation to Follow-up Activities. If Stale Deals are high despite high Calls, identify the behavior as \"New-Lead Bias\" (neglecting the middle of the funnel).\n\nThe Closing Friction: Compare Meetings to Won/Lost Deals. If Meetings are high but Wins are low, focus coaching on \"Closing Mechanics\" or \"Deal Qualification.\"\n\nOutput Format (Strict JSON):\nJSON\n{\n \"performance_summary\": \"Summarize the rep's 'Sales Rhythm' (e.g., 'High-volume hunter but low-discipline closer').\",\n \"strengths\": \"Identify the specific behavioral habit that is driving current wins.\",\n \"risks\": \"Highlight the 'Pipeline Leakage'\u2014specifically why deals are going Stale or being Lost.\",\n \"coaching_advice\": \"Provide one 'How-To' for a behavioral shift (e.g., Time-blocking, lead scoring, or objection handling).\",\n \"next_week_actions\": \"Three measurable tasks, including a specific goal for 'Stale Deal' recovery.\"\n}",
        "batching": {},
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.9
    },
    {
      "id": "90fa2d19-8476-418c-b514-dd0a92fd5cc3",
      "name": "Send Weekly Coaching Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        6528,
        1536
      ],
      "parameters": {
        "message": "=<div style=\"font-family:Arial,Helvetica,sans-serif;background:#f6f8fb;padding:20px;\">   <div style=\"max-width:700px;margin:auto;background:#ffffff;border-radius:10px;padding:25px;\">          <h2 style=\"color:#2b6cb0;margin-bottom:5px;\">        Weekly Sales Rep Behavioral Report     </h2>     <p style=\"color:#666;margin-top:0;\">Automated coaching insights & performance analysis</p>      <hr style=\"border:none;border-top:1px solid #eee;margin:20px 0;\">      <h3 style=\"color:#333;\"> Activity vs Outcomes</h3>     <img src=\"{{ $json.chartUrl }}\" style=\"width:100%;border-radius:8px;\">      <hr style=\"border:none;border-top:1px solid #eee;margin:25px 0;\">      <h3 style=\"color:#2d3748;\">Performance Summary</h3>     <p style=\"line-height:1.6;color:#444;\">       {{ $json.performance_summary }}     </p>      <h3 style=\"color:#2d3748;\"> Strengths</h3>     <p style=\"line-height:1.6;color:#444;\">       {{ $json.strengths }}     </p>      <h3 style=\"color:#2d3748;\"> Risks</h3>     <p style=\"line-height:1.6;color:#444;\">       {{ $json.risks }}     </p>      <hr style=\"border:none;border-top:1px solid #eee;margin:25px 0;\">      <h3 style=\"color:#2b6cb0;\"> Coaching Advice</h3>     <div style=\"background:#f0f7ff;padding:15px;border-radius:8px;color:#333;\">       {{ $json.coaching_advice }}     </div>      <h3 style=\"color:#2b6cb0;\"> Next Week Action Plan</h3>     <div style=\"background:#f0fff4;padding:15px;border-radius:8px;color:#333;\">       {{ $json.next_week_actions }}     </div>      <hr style=\"border:none;border-top:1px solid #eee;margin:30px 0;\">      <p style=\"font-size:12px;color:#888;text-align:center;\">       Generated automatically by Sales Rep Behavioral Pattern Analyzer     </p>    </div> </div>",
        "options": {},
        "subject": "Weekly Sales Rep Performance & Coaching Report"
      },
      "typeVersion": 2.2
    },
    {
      "id": "49931d87-4a11-49c3-b09e-7b3af06f8cb5",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5296,
        928
      ],
      "parameters": {
        "color": 7,
        "width": 1476,
        "height": 912,
        "content": "## AI Coaching, Chart & Report Delivery\nGenerate AI coaching insights, create the performance chart URL, compile the final sales report, and automatically email a weekly performance summary to stakeholders."
      },
      "typeVersion": 1
    },
    {
      "id": "bf490bf2-be61-483d-b1cf-4729b1d06522",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        928,
        -272
      ],
      "parameters": {
        "width": 944,
        "height": 704,
        "content": "## Workflow Overview: Sales Rep Behavioral Pattern Analyzer\nThis professional automation transforms raw CRM data into high-impact coaching insights. By analyzing the \"rhythm\" of sales activity rather than just final outcomes, it uncovers the specific behavioral shifts needed to improve conversion rates and pipeline health.\n\n## How It Works\nThe workflow pulls weekly activity and opportunity data from Salesforce to calculate core performance metrics. A logic engine then identifies behavioral patterns\u2014such as the **Neglect Index** (follow-up gaps) and **New-Lead Bias**. These signals are processed by a Groq-powered AI engine to generate strengths, risks, and actionable coaching advice, which is delivered via an automated email report featuring visual performance charts.\n\n## Setup Steps\n**Data Extraction:** Pull recent Salesforce Opportunities and filter associated Events and Tasks using a JS ID-extractor.\n**Activity Merging:** Combine Events and Tasks into a unified stream and map them back to specific Opportunity records.\n**Metrics Calculation:** Use JS to aggregate \"touches\" and \"activity gaps\" into rep-level performance and pipeline health indicators.\n**AI & Visualization:** Generate a QuickChart URL for data trends and use an LLM to transform behavioral signals into coaching insights.\n**Data Formatting:** Flatten the nested AI and Chart outputs into a clean, structured payload.\n**Email Delivery:** Send the final automated behavioral report and visual chart to stakeholders."
      },
      "typeVersion": 1
    },
    {
      "id": "c005546d-d91d-473f-973a-83649f3ea858",
      "name": "Build Opportunity ID List",
      "type": "n8n-nodes-base.code",
      "position": [
        1968,
        1520
      ],
      "parameters": {
        "jsCode": "const oppIds = $input.all().map(item => item.json.Id);\nconst quoted = oppIds.map(id => `'${id}'`);\nconst oppIdString = quoted.join(\",\");\n\nreturn [\n  {\n    json: {\n      opportunityIdsString: oppIdString\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "55626c6e-fb7a-4b43-a666-edf452e3c90b",
      "name": "Fetch Opportunities",
      "type": "n8n-nodes-base.salesforce",
      "position": [
        1728,
        1424
      ],
      "parameters": {
        "query": "=SELECT\n    Id,\n    Name,\n    OwnerId,\n    StageName,\n    Amount,\n    IsWon,\n    IsClosed,\n    CreatedDate,\n    CloseDate,\n    LastActivityDate,\n    LeadSource\nFROM Opportunity\nWHERE OwnerId = '{{ $('Get user').item.json.recentItems[0].Id }}'\nAND LastActivityDate = LAST_WEEK\nORDER BY CreatedDate DESC",
        "resource": "search"
      },
      "typeVersion": 1
    },
    {
      "id": "27ded2a6-1b7e-4551-867f-d8464496c5aa",
      "name": "Get user",
      "type": "n8n-nodes-base.salesforce",
      "position": [
        1200,
        1424
      ],
      "parameters": {
        "userId": "=",
        "resource": "user"
      },
      "typeVersion": 1
    },
    {
      "id": "3ad0a5cd-defa-4584-9d53-e1371890610b",
      "name": "Merge AI Output + Chart URL",
      "type": "n8n-nodes-base.merge",
      "position": [
        5872,
        1232
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "e7da1843-87ab-4598-8c4d-305b292ebe4c",
      "name": "Prepare Email Report Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        6496,
        1152
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "82c6ad27-e294-4322-9a08-c12ca01ea154",
              "name": "performance_summary",
              "type": "string",
              "value": "={{ $json.performance_summary }}"
            },
            {
              "id": "dd327a0c-c299-493f-95b3-57b68a200a7c",
              "name": "strengths",
              "type": "string",
              "value": "={{ $json.strengths }}"
            },
            {
              "id": "93eef261-7dfd-4e8f-aa5b-109f22874fcb",
              "name": "risks",
              "type": "string",
              "value": "={{ $json.risks }}"
            },
            {
              "id": "31355df1-450f-4ae9-81ca-7a2110e65a58",
              "name": "coaching_advice",
              "type": "string",
              "value": "={{ $json.coaching_advice }}"
            },
            {
              "id": "4334c224-7590-4bc7-9669-579a4198d0ec",
              "name": "next_week_actions",
              "type": "string",
              "value": "={{ $json.next_week_actions }}"
            },
            {
              "id": "909ce453-2102-497e-91ea-8f057d26e43c",
              "name": "chartUrl",
              "type": "string",
              "value": "={{ $json.chartUrl }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8c93296c-5043-4265-96a9-e0117c906ae6",
      "name": "Flatten AI Output",
      "type": "n8n-nodes-base.code",
      "position": [
        6176,
        1152
      ],
      "parameters": {
        "jsCode": "// Get AI node data\nconst ai = $items(\"Generate AI Coaching Insights\")[0].json.output;\n\n// Get chart node data\nconst chart = $items(\"Generate Performance Chart URL\")[0].json;\n\n// Return merged + flattened object\nreturn [{\n  json: {\n    performance_summary: ai.performance_summary,\n    coaching_advice: ai.coaching_advice,\n    next_week_actions: ai.next_week_actions,\n    risks: ai.risks,\n    strengths: ai.strengths,\n    chartUrl: chart.chartUrl\n  }\n}];"
      },
      "typeVersion": 2
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "76b6f53d-fb0d-495b-8993-cb8193cbf7f4",
  "nodeGroups": [],
  "connections": {
    "Get user": {
      "main": [
        [
          {
            "node": "Get Opportunity Metadata",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LLM Model": {
      "ai_languageModel": [
        [
          {
            "node": "Generate AI Coaching Insights",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Start Workflow": {
      "main": [
        [
          {
            "node": "Get user",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Output Schema": {
      "ai_outputParser": [
        [
          {
            "node": "Generate AI Coaching Insights",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Flatten AI Output": {
      "main": [
        [
          {
            "node": "Prepare Email Report Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Opportunities": {
      "main": [
        [
          {
            "node": "Build Opportunity ID List",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge Opportunities + Activities",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Tasks + Events": {
      "main": [
        [
          {
            "node": "Merge Opportunities + Activities",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Get Opportunity Metadata": {
      "main": [
        [
          {
            "node": "Fetch Opportunities",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Opportunity ID List": {
      "main": [
        [
          {
            "node": "Fetch Tasks for Opportunities",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Events for Opportunities",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge AI Output + Chart URL": {
      "main": [
        [
          {
            "node": "Flatten AI Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Email Report Fields": {
      "main": [
        [
          {
            "node": "Send Weekly Coaching Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Tasks for Opportunities": {
      "main": [
        [
          {
            "node": "Merge Tasks + Events",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate AI Coaching Insights": {
      "main": [
        [
          {
            "node": "Merge AI Output + Chart URL",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Fetch Events for Opportunities": {
      "main": [
        [
          {
            "node": "Merge Tasks + Events",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Generate Performance Chart URL": {
      "main": [
        [
          {
            "node": "Merge AI Output + Chart URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Metrics Per Sales Rep": {
      "main": [
        [
          {
            "node": "Generate AI Coaching Insights",
            "type": "main",
            "index": 0
          },
          {
            "node": "Generate Performance Chart URL",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Opportunities + Activities": {
      "main": [
        [
          {
            "node": "Compute Opportunity Activity Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compute Opportunity Activity Metrics": {
      "main": [
        [
          {
            "node": "Aggregate Metrics Per Sales Rep",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}