{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "5d1c5562-7ac5-4fbe-9a66-992a3d5dd9ff",
      "name": "Get a time entry",
      "type": "n8n-nodes-base.clockify",
      "onError": "continueRegularOutput",
      "position": [
        -1696,
        352
      ],
      "parameters": {
        "resource": "timeEntry",
        "operation": "get",
        "additionalFields": {}
      },
      "credentials": {
        "clockifyApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a5bce0db-f455-44fd-a62e-2a15707434ba",
      "name": "Get a balance",
      "type": "n8n-nodes-base.stripe",
      "onError": "continueRegularOutput",
      "position": [
        -1696,
        544
      ],
      "parameters": {},
      "retryOnFail": false,
      "typeVersion": 1
    },
    {
      "id": "f94b67f4-0cd3-4e8f-93e2-b26b4634101a",
      "name": "Get many orders",
      "type": "n8n-nodes-base.shopify",
      "onError": "continueRegularOutput",
      "position": [
        -1696,
        160
      ],
      "parameters": {
        "options": {},
        "operation": "getAll"
      },
      "typeVersion": 1
    },
    {
      "id": "a0ec860f-29b9-49ab-8539-c1c0dc114aeb",
      "name": "Facebook Graph API",
      "type": "n8n-nodes-base.facebookGraphApi",
      "position": [
        -1696,
        -224
      ],
      "parameters": {
        "edge": "insights",
        "node": "={{$json.account_id}}",
        "options": {
          "queryParameters": {
            "parameter": [
              {
                "name": "fields",
                "value": "spend"
              },
              {
                "name": "time_range",
                "value": "{\"since\":\"2025-02-01\",\"until\":\"2025-02-10\"}"
              },
              {
                "name": "level",
                "value": "account"
              }
            ]
          }
        },
        "graphApiVersion": "v23.0"
      },
      "credentials": {
        "facebookGraphApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7e63e486-99fd-4e4b-a146-f5e2e5014c40",
      "name": "Get a campaign",
      "type": "n8n-nodes-base.googleAds",
      "onError": "continueRegularOutput",
      "position": [
        -1696,
        -32
      ],
      "parameters": {
        "operation": "get",
        "campaignId": "12345678",
        "requestOptions": {},
        "clientCustomerId": "3650538801",
        "managerCustomerId": "12345678"
      },
      "credentials": {
        "googleAdsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7598c5dc-a583-421a-9b41-7f7e5914f90a",
      "name": "Append row in sheet1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -800,
        160
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [
            {
              "id": "timestamp",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "timestamp",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "date",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "totalRevenue",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "totalRevenue",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "totalAdSpend",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "totalAdSpend",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "facebookAdSpend",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "facebookAdSpend",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "googleAdSpend",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "googleAdSpend",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "totalTimeCost",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "totalTimeCost",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "platformFees",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "platformFees",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "softwareCosts",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "softwareCosts",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "overheadAllocation",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "overheadAllocation",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "totalCosts",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "totalCosts",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "grossProfit",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "grossProfit",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "profitMargin",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "profitMargin",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cac",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "cac",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "roas",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "roas",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "conversions",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "conversions",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "billableHours",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "billableHours",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "isProfitable",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "isProfitable",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "meetsMinROAS",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "meetsMinROAS",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [
            "id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1E5YoG_4GvHIF03UWpT6aO9dkVipikAjRx153Q66SKVk/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1E5YoG_4GvHIF03UWpT6aO9dkVipikAjRx153Q66SKVk",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1E5YoG_4GvHIF03UWpT6aO9dkVipikAjRx153Q66SKVk/edit?usp=drivesdk",
          "cachedResultName": "Autonomous Client Profit Guardian"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "8cf5f9e5-0eb9-4b88-8b77-8d8521f71e88",
      "name": "Schedule Trigger1",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1920,
        160
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 8 * * 1"
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "1d4dec25-431c-4aa0-b47d-dffbf7222cc0",
      "name": "Merge1",
      "type": "n8n-nodes-base.merge",
      "position": [
        -1248,
        112
      ],
      "parameters": {
        "numberInputs": 5
      },
      "typeVersion": 3.2
    },
    {
      "id": "180d1013-e364-4fd9-92a6-d384d3522d6b",
      "name": "Facebook ads1",
      "type": "n8n-nodes-base.set",
      "position": [
        -1472,
        -224
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "{\n  \"data\": [\n    {\n      \"account_id\": \"887041830908202\",\n      \"account_name\": \"Test Ad Account\",\n\n      \"campaign_id\": \"120210000001\",\n      \"campaign_name\": \"Lead Gen Campaign\",\n\n      \"adset_id\": \"120210000002\",\n      \"adset_name\": \"Broad Targeting\",\n\n      \"ad_id\": \"120210000003\",\n      \"ad_name\": \"Creative A\",\n\n      \"date_start\": \"2025-02-01\",\n      \"date_stop\": \"2025-02-01\",\n\n      \"impressions\": \"12543\",\n      \"reach\": \"10321\",\n      \"frequency\": \"1.21\",\n\n      \"clicks\": \"312\",\n      \"unique_clicks\": \"280\",\n      \"ctr\": \"2.49\",\n      \"unique_ctr\": \"2.71\",\n\n      \"cpc\": \"0.38\",\n      \"cpm\": \"9.52\",\n\n      \"spend\": \"118.72\",\n\n      \"actions\": [\n        { \"action_type\": \"link_click\", \"value\": \"312\" },\n        { \"action_type\": \"lead\", \"value\": \"42\" },\n        { \"action_type\": \"purchase\", \"value\": \"9\" }\n      ],\n\n      \"cost_per_action_type\": [\n        { \"action_type\": \"lead\", \"value\": \"2.83\" },\n        { \"action_type\": \"purchase\", \"value\": \"13.19\" }\n      ],\n\n      \"conversions\": [\n        { \"action_type\": \"purchase\", \"value\": \"9\" }\n      ],\n\n      \"purchase_roas\": [\n        { \"action_type\": \"purchase\", \"value\": \"3.75\" }\n      ],\n\n      \"video_play_actions\": [\n        { \"action_type\": \"video_view\", \"value\": \"4210\" }\n      ]\n    },\n\n    {\n      \"account_id\": \"887041830908202\",\n      \"account_name\": \"Test Ad Account\",\n\n      \"campaign_id\": \"120210000001\",\n      \"campaign_name\": \"Lead Gen Campaign\",\n\n      \"adset_id\": \"120210000002\",\n      \"adset_name\": \"Broad Targeting\",\n\n      \"ad_id\": \"120210000004\",\n      \"ad_name\": \"Creative B\",\n\n      \"date_start\": \"2025-02-01\",\n      \"date_stop\": \"2025-02-01\",\n\n      \"impressions\": \"8421\",\n      \"reach\": \"7004\",\n      \"frequency\": \"1.20\",\n\n      \"clicks\": \"181\",\n      \"ctr\": \"2.14\",\n\n      \"cpc\": \"0.42\",\n      \"cpm\": \"8.98\",\n\n      \"spend\": \"76.22\",\n\n      \"actions\": [\n        { \"action_type\": \"link_click\", \"value\": \"181\" },\n        { \"action_type\": \"lead\", \"value\": \"21\" }\n      ],\n\n      \"cost_per_action_type\": [\n        { \"action_type\": \"lead\", \"value\": \"3.62\" }\n      ]\n    }\n  ],\n\n  \"paging\": {\n    \"cursors\": {\n      \"before\": \"QVFIUl9abc123\",\n      \"after\": \"QVFIUl9xyz789\"\n    },\n    \"next\": \"https://graph.facebook.com/v24.0/act_887041830908202/insights?after=QVFIUl9xyz789\"\n  }\n}\n"
      },
      "typeVersion": 3.4
    },
    {
      "id": "3e1e5a60-9438-49b6-a126-932ff3929ff6",
      "name": "Google ads1",
      "type": "n8n-nodes-base.set",
      "position": [
        -1472,
        -32
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "{\n    \"results\": [\n      {\n        \"customer\": { \"id\": \"1234567890\" },\n        \"campaign\": { \"id\": \"111\", \"name\": \"Search Brand Campaign\" },\n        \"metrics\": {\n          \"costMicros\": 24500000,\n          \"clicks\": 320,\n          \"impressions\": 15400\n        },\n        \"segments\": { \"date\": \"2026-02-09\" }\n      },\n      {\n        \"customer\": { \"id\": \"1234567890\" },\n        \"campaign\": { \"id\": \"222\", \"name\": \"PMax Campaign\" },\n        \"metrics\": {\n          \"costMicros\": 78600000,\n          \"clicks\": 910,\n          \"impressions\": 45200\n        },\n        \"segments\": { \"date\": \"2026-02-09\" }\n      }\n    ]\n  }"
      },
      "typeVersion": 3.4
    },
    {
      "id": "dd114afb-6f16-4829-a54e-5ded05a7717c",
      "name": "Shopify1",
      "type": "n8n-nodes-base.set",
      "position": [
        -1472,
        160
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "{\n  \"orders\": [\n    {\n      \"id\": 1001,\n      \"order_number\": 501,\n      \"created_at\": \"2026-02-10T10:20:30Z\",\n      \"currency\": \"USD\",\n      \"financial_status\": \"paid\",\n      \"subtotal_price\": \"200.00\",\n      \"total_tax\": \"20.00\",\n      \"total_discounts\": \"10.00\",\n      \"total_price\": \"210.00\",\n      \"total_refunds\": \"0.00\",\n      \"customer\": {\n        \"id\": 9001,\n        \"email\": \"user@example.com\"\n      }\n    },\n    {\n      \"id\": 1002,\n      \"order_number\": 502,\n      \"created_at\": \"2026-02-10T11:05:00Z\",\n      \"currency\": \"USD\",\n      \"financial_status\": \"paid\",\n      \"subtotal_price\": \"350.00\",\n      \"total_tax\": \"35.00\",\n      \"total_discounts\": \"25.00\",\n      \"total_price\": \"360.00\",\n      \"total_refunds\": \"20.00\",\n      \"customer\": {\n        \"id\": 9002,\n        \"email\": \"user@example.com\"\n      }\n    },\n    {\n      \"id\": 1003,\n      \"order_number\": 503,\n      \"created_at\": \"2026-02-10T12:40:12Z\",\n      \"currency\": \"USD\",\n      \"financial_status\": \"paid\",\n      \"subtotal_price\": \"150.00\",\n      \"total_tax\": \"15.00\",\n      \"total_discounts\": \"0.00\",\n      \"total_price\": \"165.00\",\n      \"total_refunds\": \"0.00\",\n      \"customer\": {\n        \"id\": 9003,\n        \"email\": \"user@example.com\"\n      }\n    }\n  ]\n}\n"
      },
      "typeVersion": 3.4
    },
    {
      "id": "b550b321-f37a-4b2e-8cfc-e3d0d5428cde",
      "name": "Stripe1",
      "type": "n8n-nodes-base.set",
      "position": [
        -1472,
        352
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "{\n  \"object\": \"revenue_recognition_transaction\",\n  \"id\": \"revt_123456789\",\n  \"amount\": 10000,\n  \"currency\": \"usd\",\n  \"created\": 1609459200,\n  \"performance_obligations\": [\n    {\n      \"id\": \"po_123456\",\n      \"period\": {\n        \"start\": 1609459200,\n        \"end\": 1612137600\n      },\n      \"amount\": 10000,\n      \"recognized_amount\": 3226,\n      \"deferred_amount\": 6774\n    }\n  ],\n  \"source\": {\n    \"object\": \"invoice\",\n    \"id\": \"in_123456789\"\n  },\n  \"revenue_schedule\": [\n    {\n      \"period\": \"2021-01\",\n      \"amount\": 3226\n    },\n    {\n      \"period\": \"2021-02\",\n      \"amount\": 6774\n    }\n  ],\n  \"metadata\": {\n    \"product_name\": \"Premium Subscription\"\n  }\n}"
      },
      "typeVersion": 3.4
    },
    {
      "id": "ae4c8ef4-c72a-4dc1-a558-6d6d7658451b",
      "name": "Clockify1",
      "type": "n8n-nodes-base.set",
      "position": [
        -1472,
        544
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "{\n  \"json\": [\n  {\n    \"id\": \"te_1001\",\n    \"description\": \"Meta Ads optimization\",\n    \"projectId\": \"proj_client_001\",\n    \"projectName\": \"Client A - Ads\",\n    \"clientId\": \"client_001\",\n    \"clientName\": \"Client A\",\n    \"userId\": \"user_01\",\n    \"userName\": \"Media Buyer\",\n    \"billable\": true,\n    \"timeInterval\": {\n      \"start\": \"2026-02-01T09:00:00Z\",\n      \"end\": \"2026-02-01T11:30:00Z\",\n      \"duration\": \"PT2H30M\"\n    },\n    \"hours\": 2.5,\n    \"hourlyRate\": 35,\n    \"costAmount\": 87.5,\n    \"revenueAttributed\": 900\n  },\n  {\n    \"id\": \"te_1002\",\n    \"description\": \"Landing page edits\",\n    \"projectId\": \"proj_client_001\",\n    \"projectName\": \"Client A - Ads\",\n    \"clientId\": \"client_001\",\n    \"clientName\": \"Client A\",\n    \"userId\": \"user_02\",\n    \"userName\": \"Designer\",\n    \"billable\": true,\n    \"timeInterval\": {\n      \"start\": \"2026-02-01T12:00:00Z\",\n      \"end\": \"2026-02-01T14:00:00Z\",\n      \"duration\": \"PT2H\"\n    },\n    \"hours\": 2,\n    \"hourlyRate\": 25,\n    \"costAmount\": 50,\n    \"revenueAttributed\": 0\n  },\n  {\n    \"id\": \"te_1003\",\n    \"description\": \"Google Ads setup\",\n    \"projectId\": \"proj_client_002\",\n    \"projectName\": \"Client B - Search\",\n    \"clientId\": \"client_002\",\n    \"clientName\": \"Client B\",\n    \"userId\": \"user_01\",\n    \"userName\": \"Media Buyer\",\n    \"billable\": true,\n    \"timeInterval\": {\n      \"start\": \"2026-02-02T08:00:00Z\",\n      \"end\": \"2026-02-02T11:00:00Z\",\n      \"duration\": \"PT3H\"\n    },\n    \"hours\": 3,\n    \"hourlyRate\": 35,\n    \"costAmount\": 105,\n    \"revenueAttributed\": 1500\n  },\n  {\n    \"id\": \"te_1004\",\n    \"description\": \"Reporting and calls\",\n    \"projectId\": \"proj_client_002\",\n    \"projectName\": \"Client B - Search\",\n    \"clientId\": \"client_002\",\n    \"clientName\": \"Client B\",\n    \"userId\": \"user_03\",\n    \"userName\": \"Account Manager\",\n    \"billable\": false,\n    \"timeInterval\": {\n      \"start\": \"2026-02-02T12:00:00Z\",\n      \"end\": \"2026-02-02T13:30:00Z\",\n      \"duration\": \"PT1H30M\"\n    },\n    \"hours\": 1.5,\n    \"hourlyRate\": 30,\n    \"costAmount\": 45,\n    \"revenueAttributed\": 0\n  }\n]\n\n \n}\n"
      },
      "typeVersion": 3.4
    },
    {
      "id": "0526e83e-25e6-4ab3-9b10-880ce38103f2",
      "name": "Send a message",
      "type": "n8n-nodes-base.slack",
      "position": [
        -224,
        160
      ],
      "parameters": {
        "text": "=:bar_chart: *Client Profitability Report* | {{ new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) }}\n\n{{ $('Code in JavaScript').item.json.summary_text }}\n\n---\n\n{{ $('AI Profit Analyzer1').item.json.output }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C0ABU5WAKLH",
          "cachedResultName": "estateline-ai"
        },
        "otherOptions": {
          "includeLinkToWorkflow": false
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.4
    },
    {
      "id": "15161336-b9b9-4391-a454-a6cb0bc64f4e",
      "name": "AI Profit Analyzer1",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -576,
        160
      ],
      "parameters": {
        "text": "=Analyze this agency profitability data and provide actionable insights:\n\n**CLIENT BREAKDOWN:**\n{{ JSON.stringify($('Code in JavaScript').item.json.clientDetails, null, 2) }}\n\n{{ $json.breakdown }}\n\n**AGENCY METRICS:**\n{{ JSON.stringify($('Code in JavaScript').item.json.summary, null, 2) }}\n{{ JSON.stringify($('Code in JavaScript').item.json.breakdown, null, 2) }}\n\n**DATA QUALITY:**\n{{ JSON.stringify($('Code in JavaScript').item.json.dataValidation, null, 2) }}\n\nProvide your analysis in this EXACT format for Slack readability:\n\n*\ud83d\udcca EXECUTIVE SUMMARY*\n[2-3 sentences on overall financial health and key findings]\n\n*\ud83d\udea8 CRITICAL ACTIONS*\n\u2022 [Action item with specific numbers and client names]\n\u2022 [Action item with specific numbers and client names]\n\n*\u26a0\ufe0f WARNINGS*\n\u2022 [Warning with specific details]\n\u2022 [Warning with specific details]\n\n*\u2705 HEALTHY CLIENTS*\n\u2022 [Client name]: [Brief status with margin %]\n\u2022 [Client name]: [Brief status with margin %]\n\n*\ud83d\udd0d DATA ISSUES*\n\u2022 [Specific data quality concern]\n\u2022 [Specific data quality concern]\n\nIMPORTANT:\n- Use bullet points (\u2022) not dashes or numbers\n- Include specific percentages and dollar amounts\n- Keep each bullet to one line when possible\n- Flag clients by name with their risk emoji (\ud83d\udd34/\u26a0\ufe0f/\ud83d\udfe2)\n- Be direct and actionable",
        "options": {
          "systemMessage": "You are a financial analyst AI for a digital marketing agency. Analyze client profitability data and provide clear, actionable insights formatted for Slack.\n\nKEY RULES:\n1. Use the pre-calculated riskLevel (\ud83d\udd34 CRITICAL, \u26a0\ufe0f WARNING, \ud83d\udfe2 HEALTHY) from the data\n2. Focus on profitMargin and timeRatio as primary health indicators\n3. Call out specific clients by name with their numbers\n4. Format for Slack: use bullet points (\u2022), bold headers (*text*), and emojis\n5. Be concise - executives need quick insights\n6. Always include dollar amounts and percentages\n7. Explain WHY a client is flagged, not just that they are\n8. Data validation issues should be clearly separated\n\nYour output will be posted directly to Slack, so formatting matters."
        },
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "df0f315f-73ea-4627-9110-dc5bc93da6ab",
      "name": "Code in JavaScript",
      "type": "n8n-nodes-base.code",
      "position": [
        -1024,
        160
      ],
      "parameters": {
        "jsCode": "// ====================================================================\n// AGENCY PROFITABILITY CALCULATOR\n// Analyzes client profitability with proper cost allocation\n// ====================================================================\n\n// Configuration\nconst CONFIG = {\n  platformFeeRate: 0.029,      // 2.9% payment processor fee\n  monthlySoftwareCosts: 500,   // Monthly SaaS costs\n  overheadRate: 0.30,          // 30% overhead on time costs\n  minProfitMargin: 20,         // Minimum acceptable profit margin %\n  maxTimeRatio: 40             // Maximum acceptable time/revenue ratio %\n};\n\nconst allInputs = $input.all();\n\n// Initialize metrics\nconst metrics = {\n  totalAdSpend: 0,\n  facebookAdSpend: 0,\n  googleAdSpend: 0,\n  totalRevenue: 0,\n  totalConversions: 0,\n  facebookConversions: 0,\n  totalTimeCost: 0,\n  totalBillableHours: 0\n};\n\n// Track data sources for validation\nconst dataSources = {\n  facebook: false,\n  google: false,\n  shopify: false,\n  timeTracking: false\n};\n\n// Client-level tracking\nconst clients = {};\n\nconst errors = [];\nconst warnings = [];\n\n// Process each input\nfor (const item of allInputs) {\n  const json = item.json;\n  \n  // Facebook Ads\n  if (json.data && Array.isArray(json.data)) {\n    dataSources.facebook = true;\n    json.data.forEach(ad => {\n      const spend = parseFloat(ad.spend) || 0;\n      const accountName = ad.account_name || 'Unknown Client';\n      \n      // Initialize client if needed\n      if (!clients[accountName]) {\n        clients[accountName] = {\n          name: accountName,\n          adSpend: 0,\n          revenue: 0,\n          timeCost: 0,\n          billableHours: 0,\n          conversions: 0,\n          platforms: { facebook: 0, google: 0 }\n        };\n      }\n      \n      clients[accountName].adSpend += spend;\n      clients[accountName].platforms.facebook += spend;\n      metrics.totalAdSpend += spend;\n      metrics.facebookAdSpend += spend;\n      \n      // Count conversions\n      if (ad.conversions) {\n        ad.conversions.forEach(conv => {\n          if (conv.action_type === 'purchase') {\n            const convValue = parseFloat(conv.value) || 0;\n            metrics.totalConversions += convValue;\n            metrics.facebookConversions += convValue;\n            clients[accountName].conversions += convValue;\n          }\n        });\n      }\n      \n      // Calculate revenue from ROAS\n      if (ad.purchase_roas) {\n        ad.purchase_roas.forEach(roas => {\n          if (roas.action_type === 'purchase') {\n            const revenue = spend * (parseFloat(roas.value) || 0);\n            metrics.totalRevenue += revenue;\n            clients[accountName].revenue += revenue;\n          }\n        });\n      }\n    });\n  }\n  \n  // Google Ads\n  if (json.results && Array.isArray(json.results)) {\n    dataSources.google = true;\n    json.results.forEach(campaign => {\n      if (campaign.metrics) {\n        const spend = (campaign.metrics.costMicros || 0) / 1000000;\n        const campaignName = campaign.campaign?.name || 'Unknown Campaign';\n        \n        // Try to map to client (simplified - you may need better mapping logic)\n        const clientName = 'Google Ads Client';\n        \n        if (!clients[clientName]) {\n          clients[clientName] = {\n            name: clientName,\n            adSpend: 0,\n            revenue: 0,\n            timeCost: 0,\n            billableHours: 0,\n            conversions: 0,\n            platforms: { facebook: 0, google: 0 }\n          };\n        }\n        \n        clients[clientName].adSpend += spend;\n        clients[clientName].platforms.google += spend;\n        metrics.totalAdSpend += spend;\n        metrics.googleAdSpend += spend;\n        \n        if (campaign.metrics.conversions) {\n          const convValue = parseFloat(campaign.metrics.conversions) || 0;\n          metrics.totalConversions += convValue;\n          clients[clientName].conversions += convValue;\n        }\n        \n        if (campaign.metrics.conversionValue) {\n          const revenue = (campaign.metrics.conversionValue || 0) / 1000000;\n          metrics.totalRevenue += revenue;\n          clients[clientName].revenue += revenue;\n        } else {\n          warnings.push('Google Ads conversion value missing - CAC may be inaccurate');\n        }\n      }\n    });\n  }\n  \n  // Shopify Orders (use as primary revenue source)\n  const facebookRevenue = metrics.totalRevenue;\n  if (json.orders && Array.isArray(json.orders)) {\n    dataSources.shopify = true;\n    let shopifyRevenue = 0;\n    json.orders.forEach(order => {\n      const totalPrice = parseFloat(order.total_price) || 0;\n      const refunds = parseFloat(order.total_refunds) || 0;\n      shopifyRevenue += (totalPrice - refunds);\n    });\n    \n    // Check for revenue discrepancy\n    if (facebookRevenue > 0 && shopifyRevenue > 0) {\n      const diff = Math.abs(shopifyRevenue - facebookRevenue);\n      const diffPercent = (diff / Math.max(shopifyRevenue, facebookRevenue)) * 100;\n      if (diffPercent > 10) {\n        warnings.push(`Revenue mismatch: Shopify ($${shopifyRevenue.toFixed(2)}) vs Facebook ($${facebookRevenue.toFixed(2)}) - ${diffPercent.toFixed(1)}% difference`);\n      }\n    }\n    \n    // Use Shopify revenue if higher (more accurate)\n    if (shopifyRevenue > metrics.totalRevenue) {\n      metrics.totalRevenue = shopifyRevenue;\n    }\n  }\n  \n  // Time Tracking\n  if (json.json && Array.isArray(json.json)) {\n    dataSources.timeTracking = true;\n    json.json.forEach(entry => {\n      const costAmount = parseFloat(entry.costAmount) || 0;\n      const hours = parseFloat(entry.hours) || 0;\n      const clientName = entry.clientName || 'Unknown Client';\n      const revenueAttributed = parseFloat(entry.revenueAttributed) || 0;\n      \n      // Initialize client if needed\n      if (!clients[clientName]) {\n        clients[clientName] = {\n          name: clientName,\n          adSpend: 0,\n          revenue: 0,\n          timeCost: 0,\n          billableHours: 0,\n          conversions: 0,\n          platforms: { facebook: 0, google: 0 }\n        };\n      }\n      \n      clients[clientName].timeCost += costAmount;\n      clients[clientName].revenue += revenueAttributed;\n      \n      metrics.totalTimeCost += costAmount;\n      metrics.totalRevenue += revenueAttributed;\n      \n      if (entry.billable) {\n        metrics.totalBillableHours += hours;\n        clients[clientName].billableHours += hours;\n      }\n    });\n  }\n}\n\n// Validate data completeness\nif (!dataSources.facebook && !dataSources.google) {\n  errors.push('No ad platform data found');\n}\nif (!dataSources.shopify && metrics.totalRevenue === 0) {\n  errors.push('No revenue data found');\n}\nif (!dataSources.timeTracking) {\n  warnings.push('No time tracking data - labor costs may be incomplete');\n}\n\n// Calculate costs\nconst platformFees = metrics.totalRevenue * CONFIG.platformFeeRate;\nconst overheadAllocation = metrics.totalTimeCost * CONFIG.overheadRate;\nconst totalCosts = metrics.totalAdSpend + metrics.totalTimeCost + platformFees + \n                   CONFIG.monthlySoftwareCosts + overheadAllocation;\n\n// Calculate key metrics\nconst cac = metrics.totalConversions > 0 ? metrics.totalAdSpend / metrics.totalConversions : 0;\nconst roas = metrics.totalAdSpend > 0 ? metrics.totalRevenue / metrics.totalAdSpend : 0;\nconst grossProfit = metrics.totalRevenue - totalCosts;\nconst profitMargin = metrics.totalRevenue > 0 ? (grossProfit / metrics.totalRevenue) * 100 : 0;\nconst timeRatio = metrics.totalRevenue > 0 ? (metrics.totalTimeCost / metrics.totalRevenue) * 100 : 0;\n\n// Determine risk level\nlet riskLevel = '\ud83d\udfe2 HEALTHY';\nlet riskReasons = [];\n\nif (profitMargin < 0) {\n  riskLevel = '\ud83d\udd34 CRITICAL';\n  riskReasons.push('negative profit margin');\n} else if (profitMargin < CONFIG.minProfitMargin) {\n  riskLevel = '\u26a0\ufe0f WARNING';\n  riskReasons.push(`low profit margin (${profitMargin.toFixed(1)}%)`);\n}\n\nif (timeRatio > CONFIG.maxTimeRatio) {\n  if (riskLevel === '\ud83d\udfe2 HEALTHY') riskLevel = '\u26a0\ufe0f WARNING';\n  riskReasons.push(`high time ratio (${timeRatio.toFixed(1)}%)`);\n}\n\nif (roas < 2.0 && metrics.totalAdSpend > 0) {\n  if (riskLevel === '\ud83d\udfe2 HEALTHY') riskLevel = '\u26a0\ufe0f WARNING';\n  riskReasons.push(`low ROAS (${roas.toFixed(2)}x)`);\n}\n\n// Calculate client-level metrics\nconst clientDetails = [];\nconst numClients = Object.keys(clients).length;\nconst softwarePerClient = numClients > 0 ? CONFIG.monthlySoftwareCosts / numClients : 0;\n\nObject.values(clients).forEach(client => {\n  const clientPlatformFees = client.revenue * CONFIG.platformFeeRate;\n  const clientOverhead = client.timeCost * CONFIG.overheadRate;\n  const clientTotalCosts = client.adSpend + client.timeCost + clientPlatformFees + \n                           softwarePerClient + clientOverhead;\n  \n  const clientProfit = client.revenue - clientTotalCosts;\n  const clientMargin = client.revenue > 0 ? (clientProfit / client.revenue) * 100 : 0;\n  const clientTimeRatio = client.revenue > 0 ? (client.timeCost / client.revenue) * 100 : 0;\n  const clientROAS = client.adSpend > 0 ? client.revenue / client.adSpend : 0;\n  \n  // Determine client risk level\n  let clientRisk = '\ud83d\udfe2 HEALTHY';\n  let clientRiskReasons = [];\n  \n  if (clientMargin < 0) {\n    clientRisk = '\ud83d\udd34 CRITICAL';\n    clientRiskReasons.push('negative margin');\n  } else if (clientMargin < CONFIG.minProfitMargin) {\n    clientRisk = '\u26a0\ufe0f WARNING';\n    clientRiskReasons.push('low margin');\n  }\n  \n  if (clientTimeRatio > CONFIG.maxTimeRatio) {\n    if (clientRisk === '\ud83d\udfe2 HEALTHY') clientRisk = '\u26a0\ufe0f WARNING';\n    clientRiskReasons.push('high time cost');\n  }\n  \n  if (clientROAS < 2.0 && client.adSpend > 0) {\n    if (clientRisk === '\ud83d\udfe2 HEALTHY') clientRisk = '\u26a0\ufe0f WARNING';\n    clientRiskReasons.push('low ROAS');\n  }\n  \n  clientDetails.push({\n    name: client.name,\n    revenue: parseFloat(client.revenue.toFixed(2)),\n    costs: {\n      adSpend: parseFloat(client.adSpend.toFixed(2)),\n      timeCost: parseFloat(client.timeCost.toFixed(2)),\n      platformFees: parseFloat(clientPlatformFees.toFixed(2)),\n      software: parseFloat(softwarePerClient.toFixed(2)),\n      overhead: parseFloat(clientOverhead.toFixed(2)),\n      total: parseFloat(clientTotalCosts.toFixed(2))\n    },\n    profit: parseFloat(clientProfit.toFixed(2)),\n    profitMargin: parseFloat(clientMargin.toFixed(2)),\n    timeRatio: parseFloat(clientTimeRatio.toFixed(2)),\n    roas: parseFloat(clientROAS.toFixed(2)),\n    billableHours: parseFloat(client.billableHours.toFixed(2)),\n    conversions: client.conversions,\n    platforms: client.platforms,\n    riskLevel: clientRisk,\n    riskReasons: clientRiskReasons.join(', ')\n  });\n});\n\n// Sort clients by profit margin (worst first for attention)\nclientDetails.sort((a, b) => a.profitMargin - b.profitMargin);\n\n// Create summary text for Slack header\nconst summaryEmoji = profitMargin >= CONFIG.minProfitMargin ? '\u2705' : profitMargin >= 0 ? '\u26a0\ufe0f' : '\ud83d\udd34';\nconst summary_text = `${summaryEmoji} *Revenue:* $${metrics.totalRevenue.toFixed(0)} | *Profit:* $${grossProfit.toFixed(0)} (${profitMargin.toFixed(1)}%) | *ROAS:* ${roas.toFixed(2)}x | ${riskLevel}`;\n\n// Return comprehensive data\nreturn [{\n  json: {\n    // Summary for Slack\n    summary_text: summary_text,\n    \n    // Client breakdown for AI analysis\n    clientDetails: clientDetails,\n    \n    // Detailed metrics for AI analysis\n    summary: {\n      totalRevenue: parseFloat(metrics.totalRevenue.toFixed(2)),\n      grossProfit: parseFloat(grossProfit.toFixed(2)),\n      profitMargin: parseFloat(profitMargin.toFixed(2)),\n      riskLevel: riskLevel,\n      riskReasons: riskReasons.join(', '),\n      roas: parseFloat(roas.toFixed(2)),\n      cac: parseFloat(cac.toFixed(2)),\n      timeRatio: parseFloat(timeRatio.toFixed(2))\n    },\n    \n    breakdown: {\n      costs: {\n        adSpend: parseFloat(metrics.totalAdSpend.toFixed(2)),\n        timeCost: parseFloat(metrics.totalTimeCost.toFixed(2)),\n        platformFees: parseFloat(platformFees.toFixed(2)),\n        software: CONFIG.monthlySoftwareCosts,\n        overhead: parseFloat(overheadAllocation.toFixed(2)),\n        total: parseFloat(totalCosts.toFixed(2))\n      },\n      adPlatforms: {\n        facebook: parseFloat(metrics.facebookAdSpend.toFixed(2)),\n        google: parseFloat(metrics.googleAdSpend.toFixed(2))\n      },\n      efficiency: {\n        billableHours: parseFloat(metrics.totalBillableHours.toFixed(2)),\n        conversions: metrics.totalConversions,\n        avgRevenuePerHour: metrics.totalBillableHours > 0 ? \n          parseFloat((metrics.totalRevenue / metrics.totalBillableHours).toFixed(2)) : 0\n      }\n    },\n    \n    dataValidation: {\n      errors: errors,\n      warnings: warnings,\n      sourcesFound: dataSources\n    }\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "129bfe9a-e1d5-4368-bbca-b4a05e852f82",
      "name": "Google Gemini Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -512,
        384
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "f6d394ee-0ff1-47e7-956a-ff4c7c8e9cbc",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1728,
        -496
      ],
      "parameters": {
        "color": 7,
        "width": 368,
        "height": 224,
        "content": "## Data ingestion\n\nFetch ad spend from Facebook and Google, revenue from Shopify and Stripe, and time tracking data from Clockify.\n\nDisable mock Set nodes and connect real API nodes directly to the Merge node before going live.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "017299a1-1224-4e6e-b92c-607fb2b50d06",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1104,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 224,
        "content": "## Processing logic\n\nMerge all sources and calculate revenue, costs, ROAS, CAC, profit margin, and time ratio per client.\n\nUpdate the CONFIG object in the code node to match your real overhead rate, software costs, and minimum margin thresholds.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "7cd6b962-db8a-46a6-82f0-29260048e247",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -624,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 224,
        "content": "## AI analysis\n\nSend structured financial data to the AI agent to generate a concise executive summary.\n\nAdjust the system message if your definition of healthy performance changes.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "925733b9-c85d-4179-9551-5acb1e386987",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -640,
        544
      ],
      "parameters": {
        "color": 7,
        "width": 368,
        "height": 176,
        "content": "## Storage and notifications\n\nAppend weekly metrics to Google Sheets for historical tracking.\n\nPost a formatted profitability summary to Slack. Ensure the Slack bot is invited to the selected channel.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "7c3325d6-9219-41a3-a5a0-828373e78e52",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        80
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 208,
        "content": "## Go live checklist\n\n- Remove all mock Set nodes  \n- Confirm client naming consistency across platforms  \n- Validate Google Sheets column mapping  \n- Review configuration values  \n- Run a manual test before enabling schedule\n"
      },
      "typeVersion": 1
    },
    {
      "id": "b7bb0126-5898-4e03-84b1-8e2f8509d5c5",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2512,
        -624
      ],
      "parameters": {
        "width": 528,
        "height": 720,
        "content": "# Agency profitability monitor\n\nCalculate client profitability by combining ad spend, revenue, and time tracking data, then store results and send an executive summary to Slack.\n\nThis workflow runs weekly and pulls data from Facebook Ads, Google Ads, Shopify, Stripe, and Clockify. It merges all inputs, calculates revenue, ad spend, labor cost, platform fees, overhead, profit margin, ROAS, CAC, and time to revenue ratio per client.\n\nIt flags unprofitable or inefficient clients based on configurable thresholds inside the code node. Results are appended to Google Sheets for historical tracking, and a structured summary is generated by an AI agent and posted to Slack.\n\n## How it works\n\n1. Fetch ad, revenue, and time tracking data.\n2. Merge all sources into one dataset.\n3. Calculate profitability and risk level per client.\n4. Store metrics in Google Sheets.\n5. Send formatted insights to Slack.\n\n## Setup steps\n\n1. Connect credentials for all API nodes.\n2. Remove mock Set nodes before going live.\n3. Update configuration values in the code node.\n4. Verify Google Sheets column headers.\n5. Test run before enabling the schedule.\n"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Merge1": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stripe1": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 3
          }
        ]
      ]
    },
    "Shopify1": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Clockify1": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 4
          }
        ]
      ]
    },
    "Google ads1": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Facebook ads1": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get a balance": {
      "main": [
        [
          {
            "node": "Clockify1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get a campaign": {
      "main": [
        [
          {
            "node": "Google ads1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get many orders": {
      "main": [
        [
          {
            "node": "Shopify1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get a time entry": {
      "main": [
        [
          {
            "node": "Stripe1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger1": {
      "main": [
        [
          {
            "node": "Facebook Graph API",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get a campaign",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get many orders",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get a time entry",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get a balance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "Append row in sheet1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Facebook Graph API": {
      "main": [
        [
          {
            "node": "Facebook ads1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Profit Analyzer1": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append row in sheet1": {
      "main": [
        [
          {
            "node": "AI Profit Analyzer1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "AI Profit Analyzer1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    }
  }
}