{
  "id": "10DIaOOdZ9QBUom9",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Sales Rep Performance Tracker",
  "tags": [],
  "nodes": [
    {
      "id": "e4b514d1-ceed-4308-bf24-a94abc9a01fc",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -736,
        -128
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "78510025-3729-4ff7-8349-a26ef82e1db1",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -544,
        -688
      ],
      "parameters": {
        "height": 528,
        "content": "## \ud83d\udce6 Fetch All Deals from HighLevel CRM\n**Action:**  \n- Retrieves all opportunities (deals) from the connected HighLevel CRM.  \n\n**Description:**  \n- Uses `getAll` operation to pull unlimited deal records.  \n- Captures key fields for performance tracking:  \n  - Deal name, stage, assigned rep, value, client/company name, and update date.  \n- Provides the raw dataset for later data cleaning and aggregation.  \n- Ensures every deal is included for accurate sales metrics.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "b92239c9-9f54-4efc-9e9c-874bf8f66759",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -256,
        32
      ],
      "parameters": {
        "height": 384,
        "content": "## \ud83d\udd0d Validate Deal Fetch Success (IF Node)\n**Action:**  \nChecks if the fetched deal records contain valid IDs.  \n\n**Description:**  \nActs as a validation gate to ensure data integrity.  \nIf `deal_id` is present \u2192 continue to processing.  \nIf missing or empty \u2192 trigger the error logging branch.  \nPrevents downstream errors by stopping invalid data early.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "5e0496ef-bb97-44fa-8df4-ed773f3e7eb9",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        64,
        496
      ],
      "parameters": {
        "height": 448,
        "content": "## \ud83d\udea8 Log Fetch or Validation Errors (Error Handling Node)\n**Action:**  \nAppends failed records or errors into a Google Sheets error log.  \n\n**Description:**  \nLogs all failed deal records for manual review or debugging.  \nEach log includes `error_id` and `error` details for traceability.  \nProvides a centralized view of all CRM fetch or data validation issues.  \nHelps maintain clean and reliable automation flow.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "8f2b9f85-8750-4d8b-bba2-cb240a4954fe",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1056,
        400
      ],
      "parameters": {
        "height": 432,
        "content": "## \ud83e\udde0 GPT-4o Model Configuration\n**Action:**  \n- Configures GPT-4o (Azure OpenAI) model for language generation.  \n\n**Description:**  \n- Defines system role for generating short, friendly, motivational messages.  \n- Ensures tone consistency and optimized token usage.  \n- Connects to downstream LangChain AI Agent node for contextual outputs.  \n- Enables scalable and automated message generation via OpenAI.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "db313017-c7f5-4671-b994-c56e75d006ae",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        672,
        -912
      ],
      "parameters": {
        "height": 544,
        "content": "## \ud83e\uddfe Generate Notion Performance Dashboard\n**Action:**  \n- Creates a personalized performance page for each rep in Notion.  \n\n**Description:**  \n- Generates a Notion page titled \u201c{rep_id} - Sales Rep Performance Tracker\u201d.  \n- Includes performance stats, motivational summary, and leaderboard context:  \n  - Total deals, total value, deals won, average value.  \n- Explains business value: transparent visibility \u2192 healthy competition.  \n- Refreshes daily for up-to-date tracking and motivation.  \n"
      },
      "typeVersion": 1
    },
    {
      "id": "08277d6f-c97f-46dd-ace1-8dc01a2ef297",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        624,
        176
      ],
      "parameters": {
        "height": 416,
        "content": "## \u2699\ufe0f Transform Data for AI Input\n**Action:**  \n- Prepares formatted JSON data for AI-based Slack message generation.  \n\n**Description:**  \n- Collects summarized data per rep and flattens it for GPT input.  \n- Returns each record as an individual AI-friendly item.  \n- Ensures smooth handoff to GPT node for text generation.  \n- Critical for maintaining consistency between stats and motivational messages.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "9518f7ae-ea95-44e1-af79-291151d1124a",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1168,
        -624
      ],
      "parameters": {
        "height": 544,
        "content": "## \ud83e\udd16 AI-Generated Motivational Slack Messages\n**Action:**  \n- Uses GPT-4o via LangChain to create custom Slack messages per rep.  \n\n**Description:**  \n- Takes summarized performance data and crafts 2\u20133 line motivational blurbs.  \n- Includes emojis \ud83c\udfaf\ud83d\udd25\ud83d\udcaa for energy and tone.  \n- Mentions rep_id and highlights achievements positively.  \n- Keeps output concise for Slack readability.  \n- Human-like, encouraging messages improve team morale and competition.  \n"
      },
      "typeVersion": 1
    },
    {
      "id": "e052b33a-3d29-4eb1-aa8a-f497c121d9d1",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1472,
        192
      ],
      "parameters": {
        "height": 464,
        "content": "## \ud83d\udcac Notify Sales Team in Slack\n**Action:**  \n- Sends AI-generated motivational summaries to the Slack channel or user.  \n\n**Description:**  \n- Posts formatted messages directly in Slack via Slack API.  \n- Targets specific user or sales channel for transparency.  \n- Reinforces accountability and recognition.  \n- Builds engagement and team motivation with daily leaderboard updates.  \n"
      },
      "typeVersion": 1
    },
    {
      "id": "6956ad52-fc95-43b7-8194-fd952b56aea9",
      "name": "Fetch All Deals from HighLevel CRM",
      "type": "n8n-nodes-base.highLevel",
      "position": [
        -464,
        -128
      ],
      "parameters": {
        "filters": {},
        "resource": "opportunity",
        "operation": "getAll",
        "requestOptions": {}
      },
      "credentials": {
        "highLevelOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "bad87d8a-9bf2-4194-995f-2619a222975f",
      "name": " Validate Deal Fetch Success",
      "type": "n8n-nodes-base.if",
      "position": [
        -192,
        -128
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "e2adb005-2b3c-4d1e-8445-442df1fe925a",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.id }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "8c390668-b627-4943-8d4a-adc0c6f84cd9",
      "name": " Log Fetch or Validation Errors ",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        128,
        320
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [
            {
              "id": "error_id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "error_id",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "error",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "error",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "error_id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1338537721,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Uldk_4BxWbdZTDZxFUeohIfeBmGHHqVEl9Ogb0l6R8Y/edit#gid=1338537721",
          "cachedResultName": "error log sheet"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1Uldk_4BxWbdZTDZxFUeohIfeBmGHHqVEl9Ogb0l6R8Y",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1Uldk_4BxWbdZTDZxFUeohIfeBmGHHqVEl9Ogb0l6R8Y/edit?usp=drivesdk",
          "cachedResultName": "Interviewer Brief Pack "
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "616feb11-a8c0-4955-9202-ed1a12e34208",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        32,
        -704
      ],
      "parameters": {
        "height": 544,
        "content": "## \ud83e\uddf9 Clean & Structure Deal Data\n**Action:**  \n- Transforms raw CRM data into clean, structured records.  \n\n**Description:**  \n- Extracts and normalizes key fields into a consistent schema:  \n  - Deal ID, Rep ID, Client Name, Status, Stage ID, Value, Updated At.  \n- Removes nulls and handles missing data (e.g., \u201cUnassigned\u201d, \u201cUnknown\u201d).  \n- Prepares the dataset for further analysis and grouping by rep.  \n- Simplifies the JSON format for easy downstream processing.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "c205c167-2333-43a9-a80b-43ebd068559d",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        304,
        -672
      ],
      "parameters": {
        "height": 512,
        "content": "## \ud83d\udcca Summarize Sales by Representative\n**Action:**  \n- Aggregates deal-level data into performance summaries per sales rep.  \n\n**Description:**  \n- Groups deals by `rep_id` and computes:  \n  - Total deals handled  \n  - Total deal value  \n  - Total won deals  \n  - Average deal value per rep  \n- Provides a summarized dataset for leaderboard and performance visualization.  \n- Enables tracking of who\u2019s closing deals effectively and who needs support.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "eda6d196-9842-46f0-b077-31ab9ccd40fd",
      "name": "Clean & Structure Deal Data",
      "type": "n8n-nodes-base.code",
      "position": [
        128,
        -128
      ],
      "parameters": {
        "jsCode": "const deals = $input.all().map(i => i.json);\n\nconst cleaned = deals.map(d => ({\n  deal_id: d.id,\n  deal_name: d.name,\n  rep_id: d.assignedTo || \"Unassigned\",\n  status: d.status,\n  stage_id: d.pipelineStageId,\n  value: d.monetaryValue || 0,\n  client_name: d.relations?.[0]?.contactName || \"Unknown\",\n  company_name: d.relations?.[0]?.companyName || \"\",\n  updated_at: d.updatedAt\n}));\n\nreturn cleaned.map(c => ({ json: c }));\n"
      },
      "typeVersion": 2
    },
    {
      "id": "de0e86ce-8d99-40bb-8e9a-a25773785f72",
      "name": " Summarize Sales by Representative",
      "type": "n8n-nodes-base.code",
      "position": [
        368,
        -128
      ],
      "parameters": {
        "jsCode": "const deals = $input.all().map(i => i.json);\n\n// Object to hold totals per rep\nconst summary = {};\n\nfor (const d of deals) {\n  const rep = d.rep_id || \"Unassigned\";\n  if (!summary[rep]) {\n    summary[rep] = {\n      rep_id: rep,\n      total_deals: 0,\n      total_value: 0,\n      won_deals: 0,\n    };\n  }\n\n  summary[rep].total_deals += 1;\n  summary[rep].total_value += d.value || 0;\n\n  // If your GHL marks won deals as 'won' or similar\n  if (d.status && d.status.toLowerCase().includes(\"won\")) {\n    summary[rep].won_deals += 1;\n  }\n}\n\n// Convert to array for output\nreturn Object.values(summary).map(rep => ({\n  json: {\n    rep_id: rep.rep_id,\n    total_deals: rep.total_deals,\n    total_value: rep.total_value,\n    won_deals: rep.won_deals,\n    avg_value: rep.total_value / rep.total_deals,\n  },\n}));\n"
      },
      "typeVersion": 2
    },
    {
      "id": "4014211b-b50d-46d2-a0dc-dba3e3bb7766",
      "name": "Generate Notion Performance Dashboard",
      "type": "n8n-nodes-base.notion",
      "position": [
        752,
        -336
      ],
      "parameters": {
        "title": "={{ $json[\"rep_id\"] }} - Sales Rep Performance Tracker\n",
        "pageId": {
          "__rl": true,
          "mode": "id",
          "value": "265802b91fa080f69fffd0fc1daffe3d"
        },
        "simple": false,
        "blockUi": {
          "blockValues": [
            {
              "text": {
                "text": [
                  {
                    "text": "={{ $json[\"rep_id\"] }} - Sales Rep Performance Tracker\n",
                    "annotationUi": {}
                  }
                ]
              },
              "type": "heading_1",
              "richText": true
            },
            {
              "textContent": "=\ud83d\udc4b Hello {{$json[\"rep_id\"]}}, here\u2019s your latest performance update! \ud83c\udf89  \n\n**Sales Rep Performance Tracker**  \n\ud83d\udd0e **Problem:** No visibility into which rep is actually performing well.  \n\u2705 **Solution:** Track your daily performance, updated every midnight.\n\nHere\u2019s your performance summary for today:  \n- \ud83d\udcca **Total Deals:** {{$json[\"total_deals\"]}}  \n- \ud83d\udcb0 **Total Value:** {{$json[\"total_value\"]}}  \n- \ud83c\udfc6 **Deals Won:** {{$json[\"won_deals\"]}}  \n- \ud83d\udcc8 **Average Deal Value:** {{$json[\"avg_value\"]}}\n\n\ud83d\udcc5 **Next Update:** The leaderboard will be refreshed every day at midnight to show your ranking alongside your peers.  \n\n**Value:** Transparent performance insights \u2192 healthy competition \u2192 better accountability.  \n\nKeep up the great work and aim for the top! \ud83d\ude80  \nStatus: *Ongoing* |"
            }
          ]
        },
        "options": {}
      },
      "credentials": {
        "notionApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "4c6b43f1-6397-46df-b4e2-880fb400dcb9",
      "name": " Transform Data for AI Input",
      "type": "n8n-nodes-base.code",
      "position": [
        704,
        0
      ],
      "parameters": {
        "jsCode": "// Collect all incoming data\nconst allData = items.map(item => item.json);\n\n// Transform and flatten the structure\nconst formattedData = allData.map(item => ({\n  rep_id: item.rep_id,\n  total_deals: item.total_deals,\n  total_value: item.total_value,\n  won_deals: item.won_deals,\n  avg_value: item.avg_value\n}));\n\n// Return each record as a separate item (AI node-friendly)\nreturn formattedData.map(data => ({ json: data }));\n"
      },
      "typeVersion": 2
    },
    {
      "id": "891ba41f-f222-4a41-b557-14808b3fabc4",
      "name": "GPT-4o Model Configuration",
      "type": "@n8n/n8n-nodes-langchain.lmChatAzureOpenAi",
      "position": [
        1072,
        224
      ],
      "parameters": {
        "model": "gpt-4o",
        "options": {}
      },
      "credentials": {
        "azureOpenAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e676ef5c-e9f9-4b2b-b6b7-8a52b8b91179",
      "name": " AI-Generated Motivational Slack Messages",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1088,
        0
      ],
      "parameters": {
        "text": "=Generate a motivational Slack message for the following sales reps based on their performance data:\n\n{{JSON.stringify($json)}}\n\nFormat it like this:\n- Use emojis \ud83c\udfaf\ud83d\udd25\ud83d\udcaa for motivation\n- Mention each rep_id and highlight their performance briefly\n- Keep it under 2\u20133 lines per rep\n",
        "options": {
          "systemMessage": "=You are a motivational message generator for a sales team. \nYou will receive sales rep data including total deals, total value, won deals, and average deal value.\nUse this data to create short, positive, and personalized motivational messages suitable for posting in Slack. \nKeep it friendly, energetic, and relevant to the rep\u2019s performance.\n"
        },
        "promptType": "define"
      },
      "typeVersion": 2.1
    },
    {
      "id": "45ff779d-dc4e-4a83-bcfc-5197aecdc91f",
      "name": "Notify Sales Team in Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        1520,
        0
      ],
      "parameters": {
        "text": "={{ $json.output }}",
        "user": {
          "__rl": true,
          "mode": "list",
          "value": "U09HMPVD466",
          "cachedResultName": "newscctv22"
        },
        "select": "user",
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "d7102749-1f49-4575-a6cc-b80eb1664bbe",
  "connections": {
    "GPT-4o Model Configuration": {
      "ai_languageModel": [
        [
          {
            "node": " AI-Generated Motivational Slack Messages",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Clean & Structure Deal Data": {
      "main": [
        [
          {
            "node": " Summarize Sales by Representative",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    " Transform Data for AI Input": {
      "main": [
        [
          {
            "node": " AI-Generated Motivational Slack Messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    " Validate Deal Fetch Success": {
      "main": [
        [
          {
            "node": "Clean & Structure Deal Data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": " Log Fetch or Validation Errors ",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    " Summarize Sales by Representative": {
      "main": [
        [
          {
            "node": "Generate Notion Performance Dashboard",
            "type": "main",
            "index": 0
          },
          {
            "node": " Transform Data for AI Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch All Deals from HighLevel CRM": {
      "main": [
        [
          {
            "node": " Validate Deal Fetch Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Fetch All Deals from HighLevel CRM",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    " AI-Generated Motivational Slack Messages": {
      "main": [
        [
          {
            "node": "Notify Sales Team in Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}