{
  "id": "3nzbPNbVwu4M4F4g",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Product Health Monitoring & Incident Management system final",
  "tags": [
    {
      "id": "6YEf66QgUoe2H0xV",
      "name": "postgresql",
      "createdAt": "2025-11-20T18:05:52.192Z",
      "updatedAt": "2025-11-20T18:05:52.192Z"
    },
    {
      "id": "S94PaeZJiPKn5Ei9",
      "name": "gmail",
      "createdAt": "2025-08-14T09:57:18.678Z",
      "updatedAt": "2025-08-14T09:57:18.678Z"
    },
    {
      "id": "SenQnwINjVwewgLY",
      "name": "slack",
      "createdAt": "2025-11-20T18:05:28.955Z",
      "updatedAt": "2025-11-20T18:05:28.955Z"
    },
    {
      "id": "T75wN7b2Y1Q2X3nH",
      "name": "alert",
      "createdAt": "2025-11-20T18:06:09.140Z",
      "updatedAt": "2025-11-20T18:06:09.140Z"
    },
    {
      "id": "Unbn35P90TtJuAQC",
      "name": "email",
      "createdAt": "2025-08-14T09:57:13.394Z",
      "updatedAt": "2025-08-14T09:57:13.394Z"
    },
    {
      "id": "bX1tZbypCr5HBJMz",
      "name": "product management",
      "createdAt": "2025-11-20T18:06:00.432Z",
      "updatedAt": "2025-11-20T18:06:00.432Z"
    },
    {
      "id": "erKAjTLp0jWx08QX",
      "name": "notions",
      "createdAt": "2025-11-16T23:07:16.962Z",
      "updatedAt": "2025-11-16T23:07:16.962Z"
    },
    {
      "id": "qCgJLisgQ1EsyJ62",
      "name": "sql",
      "createdAt": "2025-11-20T18:05:47.931Z",
      "updatedAt": "2025-11-20T18:05:47.931Z"
    }
  ],
  "nodes": [
    {
      "id": "b26501bd-bb30-4601-87bd-b91fe451e5ca",
      "name": "log incident",
      "type": "n8n-nodes-base.postgres",
      "position": [
        624,
        0
      ],
      "parameters": {
        "query": "INSERT INTO incidents (\n  metric_name,\n  dimension,\n  dimension_value,\n  date_start,\n  baseline_value,\n  actual_value,\n  delta_pct,\n  severity,\n  raw_context\n)\nVALUES (\n  '{{$json.metric_name}}',\n  'global',\n  'all',\n  '{{$json.date}}',\n  {{$json.baseline_value}},\n  {{$json.actual_value}},\n  {{$json.delta_pct}},\n  '{{$json.severity}}',\n  '{{ JSON.stringify($json) }}'::jsonb\n)\nRETURNING incident_id;\n",
        "options": {
          "queryReplacement": "={{ $json.metric_name }}, {{$json.date}}, {{$json.baseline_value}}, {{$json.actual_value}}, {{$json.delta_pct}}, {{$json.severity}}, {{ JSON.stringify($json) }}"
        },
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "761d5140-0439-4487-8246-f68109d3ca51",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -592,
        -720
      ],
      "parameters": {
        "width": 496,
        "height": 1264,
        "content": "## How it works\nThis workflow monitors product health by automatically scanning key revenue and usage metrics, detecting anomalies, and alerting the team only when something looks wrong.\n\nEvery day, the system queries your Postgres (Supabase) database for recent product metrics (e.g. churn MRR, feature usage). A JavaScript step applies a simple statistical model (baseline window + z-score / delta %) to decide whether the latest value is abnormal compared to the last few days.\n\nIf no anomaly is detected, the workflow stops silently and nothing is sent \u2014 no noise, no false alerts.\n\nIf an anomaly is detected, the workflow creates a structured incident in the incidents table (with metric name, baseline, actual value, delta %, severity and raw JSON context). It then sends a Slack alert and an email with all the key details so the product team can react quickly. Optionally, the incident can also be mirrored into a Notion database to keep a lightweight incident log for product documentation or postmortems.\n\nAdditional workflows can be plugged in on top of this foundation (e.g. root cause analysis, daily incident recap) by simply reading from the same incidents table.\n\n## Setup steps:\n\n- Configure your Postgres (Supabase) credentials in n8n and run the provided SQL script to create the core tables (accounts, revenue_events, product_usage_events, incidents, system_logs) and sample data.\n\n- In the Postgres nodes, point the connection to your Supabase database and verify that a simple SELECT 1; query works.\n\n- Add your Slack credentials (or bot token) in n8n and set the target channel for alerts (e.g. #product-health-alerts).\n\n- Add your email credentials (Gmail or SMTP) and set the \u201cFrom\u201d and \u201cTo\u201d addresses for incident alerts.\n\n- (Optional) Add your Notion integration token and database ID if you want each incident to also be logged as a Notion page.\n\n- Configure your AI provider API key (OpenAI or compatible) if you plan to add an automated \u201croot cause summary\u201d workflow on top of this system.\n\n- Adjust the cron triggers for the health checks (e.g. once per day in the morning) according to your monitoring needs.\n\n- Run a test by forcing an anomaly (or inserting a fake incident) and confirm that:\n\n- a row is created in the incidents table,\n\n- the Slack alert is posted,\n\n- the incident email is received, \n\n- and the optional Notion entry is created if configured."
      },
      "typeVersion": 1
    },
    {
      "id": "e6b2f6e1-8fdd-408a-a6c4-cff35d6446fd",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -64,
        -128
      ],
      "parameters": {
        "color": 7,
        "width": 1504,
        "height": 320,
        "content": "## 1) Daily revenue health\nChecks for MRR anomalies. Logs incident + notifies Product Team."
      },
      "typeVersion": 1
    },
    {
      "id": "6d5d041c-5253-4489-9078-8e5ef64654ec",
      "name": "daily usage metrics",
      "type": "n8n-nodes-base.postgres",
      "position": [
        208,
        320
      ],
      "parameters": {
        "query": "SELECT\n  date,\n  active_accounts,\n  accounts_viewing_dashboard,\n  accounts_using_alerts\nFROM daily_usage_metrics\nWHERE date >= current_date - INTERVAL '30 days'\nORDER BY date;\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "bba1ffc6-b558-4bec-a68d-5238c492b8b2",
      "name": "anomalies",
      "type": "n8n-nodes-base.code",
      "position": [
        416,
        320
      ],
      "parameters": {
        "jsCode": "const rows = items.map(item => item.json);\nif (rows.length < 8) return [];\n\nrows.sort((a, b) => new Date(a.date) - new Date(b.date));\n\nconst values = rows.map(r => parseFloat(r.accounts_using_alerts || 0));\nconst lastIndex = rows.length - 1;\nconst lastRow = rows[lastIndex];\nconst lastValue = values[lastIndex];\n\nconst window = 7;\nconst baselineValues = values.slice(lastIndex - window, lastIndex);\n\nconst mean = baselineValues.reduce((s, v) => s + v, 0) / baselineValues.length;\nconst variance = baselineValues\n  .map(v => Math.pow(v - mean, 2))\n  .reduce((s, v) => s + v, 0) / baselineValues.length;\nconst std = Math.sqrt(variance);\n\nif (std === 0) return [];\n\nconst z = (lastValue - mean) / std;\nconst deltaPct = mean === 0 ? null : ((lastValue - mean) / mean) * 100;\n\n// Ici on veut d\u00e9tecter UNE BAISSE (usage qui s'effondre)\nif (z < -2) {\n  const anomaly = {\n    date: lastRow.date,\n    metric_name: 'accounts_using_alerts',\n    baseline_value: mean,\n    actual_value: lastValue,\n    z_score: z,\n    delta_pct: deltaPct,\n    severity:\n      deltaPct !== null && deltaPct < -50 ? 'high'\n      : deltaPct !== null && deltaPct < -25 ? 'medium'\n      : 'low',\n  };\n  return [{ json: anomaly }];\n}\n\nreturn [];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "9ff740fb-45fc-428b-9695-6d88d3f9ee60",
      "name": "insert incidents",
      "type": "n8n-nodes-base.postgres",
      "position": [
        624,
        320
      ],
      "parameters": {
        "query": "INSERT INTO incidents (\n  metric_name,\n  dimension,\n  dimension_value,\n  date_start,\n  date_end,\n  baseline_value,\n  actual_value,\n  delta_pct,\n  severity,\n  raw_context\n)\nVALUES (\n  $1, NULL, NULL, $2, $2, $3, $4, $5, $6, $7\n)\nRETURNING incident_id;\n",
        "options": {
          "queryReplacement": "={{ $json.metric_name }}, {{ $json.date }}, {{ $json.baseline_value }}, {{ $json.actual_value }}, {{ $json.delta_pct }}, {{ $json.severity }}, {{ JSON.stringify($json) }}"
        },
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "c1dd8022-c920-4be4-94b9-60d8145c2a92",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -64,
        224
      ],
      "parameters": {
        "color": 7,
        "width": 1504,
        "height": 320,
        "content": "## 2) Daily usage health\nMonitors feature adoption. Detects unusual drops in usage patterns."
      },
      "typeVersion": 1
    },
    {
      "id": "68871ce8-020e-4afc-95c3-f7822f4c6ecb",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1472,
        -128
      ],
      "parameters": {
        "color": 7,
        "width": 2160,
        "height": 320,
        "content": "##  3) Root Cause & Summary\nEnriches incidents with country/plan breakdown + AI-generated hypothesis."
      },
      "typeVersion": 1
    },
    {
      "id": "70091a50-cdf2-42d4-bc03-4d4381ee2b6b",
      "name": "select open incident",
      "type": "n8n-nodes-base.postgres",
      "position": [
        1680,
        0
      ],
      "parameters": {
        "query": "SELECT *\nFROM incidents\nWHERE status = 'open'\nORDER BY detected_at\nLIMIT 1;\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6,
      "alwaysOutputData": true
    },
    {
      "id": "fb59e52b-1a98-48ce-92f3-2cc0f3d42569",
      "name": "revenue by country",
      "type": "n8n-nodes-base.postgres",
      "position": [
        2176,
        -112
      ],
      "parameters": {
        "query": "SELECT a.country, SUM(r.mrr_delta) AS churn_mrr\nFROM revenue_events r\nJOIN accounts a ON a.account_id = r.account_id\nWHERE r.event_type = 'churn'\n  AND r.event_date BETWEEN $1::date - INTERVAL '3 days'\n                      AND $1::date\nGROUP BY a.country\nORDER BY churn_mrr DESC;\n",
        "options": {
          "queryReplacement": "=\"queryReplacement\": \"$1 \u2192 {{ $json.date_start }}\""
        },
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "24289482-8a7a-4244-ad54-12b902dabb10",
      "name": "revenue by plan",
      "type": "n8n-nodes-base.postgres",
      "position": [
        2176,
        32
      ],
      "parameters": {
        "query": "SELECT a.plan, SUM(r.mrr_delta) AS churn_mrr\nFROM revenue_events r\nJOIN accounts a ON a.account_id = r.account_id\nWHERE r.event_type = 'churn'\n  AND r.event_date BETWEEN $1::date - INTERVAL '3 days'\n                      AND $1::date\nGROUP BY a.plan\nORDER BY churn_mrr DESC;\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "8db7fbff-dfa2-4218-b55c-8abadf147f11",
      "name": "sum up/ hypothesis",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        2624,
        -80
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "GPT-4O-MINI"
        },
        "options": {},
        "responses": {
          "values": [
            {
              "content": "=You are a Product Data & Revenue Analyst.\n\nWe detected an incident:\n{{ $json.incident }}\n\nHere is churn MRR by country (top offenders first):\n{{ $json.churn_by_country }}\n\nHere is churn MRR by plan:\n{{ $json.churn_by_plan }}\n\n1. Summarize what happened in simple business language.\n2. Identify the most impacted segments (country, plan).\n3. Propose 3-5 plausible hypotheses (product issues, price changes, bugs, market events).\n4. Propose 3 concrete next steps for the Product team.\n"
            }
          ]
        },
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "a802b7e3-031a-4ed0-8137-ed9c4a8d63cb",
      "name": "root cause summary",
      "type": "n8n-nodes-base.slack",
      "position": [
        2912,
        -80
      ],
      "parameters": {
        "text": "={   \"text\": \"*Product Health \u2013 Root Cause Analysis*\\n\\nIncident:\\n- Metric: {{ $json.metric_name }}\\n- Date: {{ $json.date_start }}\\n- Severity: {{ $json.severity }}\\n\\nAI Summary:\\n{{ $json.ai_summary }}\",   \"mrkdwn\": true }",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "yourvalue",
          "cachedResultName": "product-health-monitor"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "cf9ba3bc-5f48-4be4-bed1-183731eec3e6",
      "name": "update incident status",
      "type": "n8n-nodes-base.postgres",
      "position": [
        3280,
        -80
      ],
      "parameters": {
        "query": "UPDATE incidents \nSET status = 'analyzed',\n    ai_summary = $1,\n    updated_at = NOW()\nWHERE incident_id = $2",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "d63d2cd2-ebc7-4282-bd17-3926a7813c3a",
      "name": "daily report trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1488,
        320
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "a352dc8d-f1c6-415b-a778-d0b501f050b1",
      "name": "sum up",
      "type": "n8n-nodes-base.code",
      "position": [
        1824,
        320
      ],
      "parameters": {
        "jsCode": "const rows = items\n  .map(i => i.json)\n  .filter(r => r && Object.keys(r).length > 0); \n\n\nif (rows.length === 0) {\n  return [];\n}\n\nconst summary = {};\nfor (const r of rows) {\n  const metric = r.metric_name || 'unknown_metric';\n  const severity = r.severity || 'unknown';\n  const key = metric + '|' + severity;\n  summary[key] = (summary[key] || 0) + 1;\n}\n\nconst today = new Date();\nconst yesterday = new Date(today);\nyesterday.setDate(today.getDate() - 1);\nconst dateStr = yesterday.toISOString().slice(0, 10);\n\nlet text = `Daily Product Health Report \u2013 ${dateStr}\\n\\n`;\ntext += `Total incidents: ${rows.length}\\n\\n`;\n\nfor (const [key, count] of Object.entries(summary)) {\n  const [metric, severity] = key.split('|');\n  text += `\u2022 ${metric} \u2013 ${severity}: ${count}\\n`;\n}\n\ntext += `\\n---\\nThis email was sent automatically with n8n.`;\n\nreturn [\n  {\n    json: {\n      text,\n      incidents: rows,\n    },\n  },\n];\n"
      },
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "b475d6ca-7eb2-4629-9b53-a3429a82cbcd",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1472,
        224
      ],
      "parameters": {
        "color": 7,
        "width": 1184,
        "height": 320,
        "content": "## 4) daily report\nGenerates daily global health report for leadership."
      },
      "typeVersion": 1
    },
    {
      "id": "6fe9a892-1e9f-4bc7-af5d-8f5b68e35d36",
      "name": "email alert",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1152,
        0
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=<h2>\ud83d\udea8 Product Health \u2013 Anomaly Detected</h2>  <p><strong>Metric:</strong> {{$json.metric_name}}</p> <p><strong>Date:</strong> {{$json.date_start || $json.date}}</p> <p><strong>Severity:</strong> {{$json.severity}}</p>  <p><strong>Baseline:</strong> {{$json.baseline_value}}</p> <p><strong>Actual:</strong> {{$json.actual_value}}</p> <p><strong>Delta (%):</strong> {{$json.delta_pct}}</p>  <hr> <p>This incident was logged in Supabase and synced to Notion.</p> <p>\u2014 Product Health Bot</p>",
        "options": {},
        "subject": "=\ud83d\udea8 Product Health Alert \u2013 {{$json.metric_name}}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "44d5aeab-1255-48e7-a35c-8d6cbe56199d",
      "name": "daily report email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2000,
        320
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "={{$json.text}}",
        "options": {},
        "subject": "\ud83d\udcca Product Health \u2013 Daily Report"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "745d2bd0-4bc9-4797-8eaf-c9a5d8d59777",
      "name": "root cause summary email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        3104,
        -80
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=<h2>Root Cause Summary</h2>  <p>Metric: {{$json.metric_name}}</p> <p>Severity: {{$json.severity}}</p> <p>Summary:</p>  <p>{{$json.summary}}</p>",
        "options": {},
        "subject": "Product Health \u2013 Root Cause Summary"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "7cb0f8c5-972a-4b52-898f-bfab120f105c",
      "name": "log system",
      "type": "n8n-nodes-base.postgres",
      "position": [
        1312,
        0
      ],
      "parameters": {
        "query": "INSERT INTO system_logs (workflow, status, message)\nVALUES ($1, $2, $3)\n",
        "options": {
          "queryReplacement": "Daily revenue health, success, Incident logged and notifications sent"
        },
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "5f13ff70-ebd1-4a50-a593-35f03d0e0cf8",
      "name": "Execute the SQL query",
      "type": "n8n-nodes-base.postgres",
      "position": [
        208,
        0
      ],
      "parameters": {
        "query": "SELECT\n  date,\n  new_mrr,\n  churn_mrr,\n  net_mrr_delta\nFROM daily_revenue_metrics\nWHERE date >= current_date - INTERVAL '30 days'\nORDER BY date;\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "957a6f31-5e9e-473a-8238-d18888fbdeb0",
      "name": "Trigger RH",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        0
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 8
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "df08d817-79cd-4799-8e15-1470aaedfe91",
      "name": "Update notions",
      "type": "n8n-nodes-base.notion",
      "position": [
        800,
        0
      ],
      "parameters": {
        "pageId": {
          "__rl": true,
          "mode": "url",
          "value": "https://www.notion.so/"
        },
        "options": {},
        "resource": "databasePage",
        "operation": "update"
      },
      "credentials": {
        "notionApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "2c680c1d-25c7-4fc7-9d2a-60afd028063d",
      "name": "slack notification",
      "type": "n8n-nodes-base.slack",
      "position": [
        992,
        0
      ],
      "parameters": {
        "text": "={   \"text\": \":rotating_light: *Product Health \u2013 Revenue Anomaly Detected*\\n\\n*Metric:* {{ $json.metric_name }}\\n*Date:* {{ $json.date }}\\n*Severity:* {{ $json.severity }}\\n*Baseline:* {{ $json.baseline_value }}\\n*Current:* {{ $json.actual_value }}\\n*Delta (%):* {{ $json.delta_pct }}\",   \"mrkdwn\": true }",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "yourvalue",
          "cachedResultName": "product-health-monitor"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "18dabaa1-657a-4ae4-938f-64f3897f7244",
      "name": "Condition incident",
      "type": "n8n-nodes-base.if",
      "position": [
        1856,
        0
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "32982826-f0c7-40a7-a02c-218779f1c142",
              "operator": {
                "type": "number",
                "operation": "notEmpty",
                "singleValue": true
              },
              "leftValue": "={{ $json.incident_id }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.2,
      "alwaysOutputData": false
    },
    {
      "id": "20515047-ac2d-423d-9677-a2cce8bc1d6e",
      "name": "Merge data",
      "type": "n8n-nodes-base.merge",
      "position": [
        2464,
        -80
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "ec378c84-8bcd-437f-ac78-c00a7c2b6649",
      "name": "log system final",
      "type": "n8n-nodes-base.postgres",
      "position": [
        3440,
        -80
      ],
      "parameters": {
        "query": "INSERT INTO system_logs (workflow, status, message)\nVALUES ($1, $2, $3);\n",
        "options": {
          "queryReplacement": "Root cause & summary, success, Root cause analysis generated \u2013 summary sent to Product team"
        },
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "01c6c372-4bf8-4c00-be38-88cfbff0bfad",
      "name": "anomalies check",
      "type": "n8n-nodes-base.code",
      "position": [
        416,
        0
      ],
      "parameters": {
        "jsCode": "const rows = items.map(item => item.json);\n\nif (rows.length < 8) {\n  return [];\n}\n\nrows.sort((a, b) => new Date(a.date) - new Date(b.date));\n\nconst values = rows.map(r => parseFloat(r.churn_mrr || 0));\n\nconst lastIndex = rows.length - 1;\nconst lastRow = rows[lastIndex];\nconst lastValue = values[lastIndex];\n\nconst window = 7;\nconst baselineValues = values.slice(lastIndex - window, lastIndex);\n\nconst mean = baselineValues.reduce((s, v) => s + v, 0) / baselineValues.length;\nconst variance = baselineValues\n  .map(v => Math.pow(v - mean, 2))\n  .reduce((s, v) => s + v, 0) / baselineValues.length;\nconst std = Math.sqrt(variance);\n\n\nif (std === 0) {\n  return [];\n}\n\nconst z = (lastValue - mean) / std;\nconst deltaPct = mean === 0 ? null : ((lastValue - mean) / mean) * 100;\n\nif (z > 2) {\n  const anomaly = {\n    date: lastRow.date,\n    metric_name: 'churn_mrr',\n    baseline_value: mean,\n    actual_value: lastValue,\n    z_score: z,\n    delta_pct: deltaPct,\n    severity:\n      deltaPct !== null && deltaPct > 50 ? 'high'\n      : deltaPct !== null && deltaPct > 25 ? 'medium'\n      : 'low',\n  };\n\n  return [{ json: anomaly }];\n}\n\nreturn [];\n"
      },
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "46fa29f5-e74d-49ad-a6e5-1cb9ceb3eb8a",
      "name": "Slack notification",
      "type": "n8n-nodes-base.slack",
      "position": [
        832,
        320
      ],
      "parameters": {
        "text": "={   \"text\": \":warning: *Product Health \u2013 Feature Usage Drop*\\n\\n*Metric:* {{ $json.metric_name }}\\n*Date:* {{ $json.date }}\\n*Severity:* {{ $json.severity }}\\n*Baseline accounts:* {{ $json.baseline_value }}\\n*Current accounts:* {{ $json.actual_value }}\\n*Delta (%):* {{ $json.delta_pct }}\",   \"mrkdwn\": true }",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "yourvalue",
          "cachedResultName": "product-health-monitor"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "30289ed9-1f3b-4372-ac06-85b07d34969f",
      "name": "usage health email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1040,
        320
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=<h2>\ud83d\udea8 Usage Anomaly Detected</h2>  <p><strong>Metric:</strong> {{$json.metric_name}}</p> <p><strong>Severity:</strong> {{$json.severity}}</p> <p><strong>Baseline:</strong> {{$json.baseline_value}}</p> <p><strong>Actual:</strong> {{$json.actual_value}}</p> <p><strong>Delta:</strong> {{$json.delta_pct}}%</p>  <p>Logged in Supabase + synced to Notion.</p>",
        "options": {},
        "subject": "=\ud83d\udea8 Usage Anomaly \u2013 {{$json.metric_name}}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "f52c020d-2b5e-4ca4-b5ea-7fb94fa5070b",
      "name": "Trigger CS",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        1488,
        0
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 15
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "7e760a13-f097-490c-862c-3aff18472946",
      "name": "Trigger UH",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        320
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 8,
              "triggerAtMinute": 15
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "eebfb6b0-e6f3-4d6a-a711-b1c270207116",
      "name": "log system UH",
      "type": "n8n-nodes-base.postgres",
      "position": [
        1312,
        320
      ],
      "parameters": {
        "query": "INSERT INTO system_logs (workflow, status, message)\nVALUES ($1, $2, $3);\n",
        "options": {
          "queryReplacement": "Daily usage health, success, Usage health check executed \u2013 anomaly processed + notifications sent"
        },
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "9c37e887-7186-4a2a-a2b2-b841b8d6ddef",
      "name": "Execute SQL query incident check",
      "type": "n8n-nodes-base.postgres",
      "position": [
        1664,
        320
      ],
      "parameters": {
        "query": "SELECT *\nFROM incidents\nWHERE detected_at::date = current_date - 1\nORDER BY detected_at;\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6,
      "alwaysOutputData": true
    },
    {
      "id": "36eac13a-bab7-4b21-b572-0299d00ff1c1",
      "name": "Notions database creation",
      "type": "n8n-nodes-base.notion",
      "position": [
        2240,
        320
      ],
      "parameters": {
        "title": "=\"title\": \"=Daily Report \u2013 {{ new Date().toISOString().slice(0,10) }}\"",
        "blockUi": {
          "blockValues": [
            {
              "textContent": "={{$json.text}}"
            },
            {
              "textContent": "={{ $json.incidents.length }}"
            }
          ]
        },
        "options": {},
        "resource": "databasePage",
        "databaseId": {
          "__rl": true,
          "mode": "url",
          "value": "https://www.notion.so/"
        }
      },
      "credentials": {
        "notionApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "70b6bd0a-6913-41bd-a116-4cc283b95223",
      "name": "log system final1",
      "type": "n8n-nodes-base.postgres",
      "position": [
        2448,
        320
      ],
      "parameters": {
        "query": "INSERT INTO system_logs (workflow, status, message)\nVALUES ($1, $2, $3);\n",
        "options": {
          "queryReplacement": "Daily report, success, Daily report executed \u2013 email + Notion summary created"
        },
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "d11c921e-6e03-4d93-a4de-1dd05961236c",
  "connections": {
    "sum up": {
      "main": [
        [
          {
            "node": "daily report email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "anomalies": {
      "main": [
        [
          {
            "node": "insert incidents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge data": {
      "main": [
        [
          {
            "node": "sum up/ hypothesis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Trigger CS": {
      "main": [
        [
          {
            "node": "select open incident",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Trigger RH": {
      "main": [
        [
          {
            "node": "Execute the SQL query",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Trigger UH": {
      "main": [
        [
          {
            "node": "daily usage metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "email alert": {
      "main": [
        [
          {
            "node": "log system",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "log incident": {
      "main": [
        [
          {
            "node": "Update notions",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update notions": {
      "main": [
        [
          {
            "node": "slack notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "anomalies check": {
      "main": [
        [
          {
            "node": "log incident",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "revenue by plan": {
      "main": [
        [
          {
            "node": "Merge data",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "insert incidents": {
      "main": [
        [
          {
            "node": "Slack notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Condition incident": {
      "main": [
        [
          {
            "node": "revenue by country",
            "type": "main",
            "index": 0
          },
          {
            "node": "revenue by plan",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack notification": {
      "main": [
        [
          {
            "node": "usage health email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "daily report email": {
      "main": [
        [
          {
            "node": "Notions database creation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "revenue by country": {
      "main": [
        [
          {
            "node": "Merge data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "root cause summary": {
      "main": [
        [
          {
            "node": "root cause summary email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "slack notification": {
      "main": [
        [
          {
            "node": "email alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "sum up/ hypothesis": {
      "main": [
        [
          {
            "node": "root cause summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "usage health email": {
      "main": [
        [
          {
            "node": "log system UH",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "daily usage metrics": {
      "main": [
        [
          {
            "node": "anomalies",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "daily report trigger": {
      "main": [
        [
          {
            "node": "Execute SQL query incident check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "select open incident": {
      "main": [
        [
          {
            "node": "Condition incident",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute the SQL query": {
      "main": [
        [
          {
            "node": "anomalies check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "update incident status": {
      "main": [
        [
          {
            "node": "log system final",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "root cause summary email": {
      "main": [
        [
          {
            "node": "update incident status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notions database creation": {
      "main": [
        [
          {
            "node": "log system final1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Execute SQL query incident check": {
      "main": [
        [
          {
            "node": "sum up",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}