{
  "id": "RAVj412PdMc4kTPE",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Quality data check system",
  "tags": [
    {
      "id": "fqJGztMNmqQ1Q373",
      "name": "notion",
      "createdAt": "2025-11-26T16:21:14.436Z",
      "updatedAt": "2025-11-26T16:21:14.436Z"
    },
    {
      "id": "T75wN7b2Y1Q2X3nH",
      "name": "alert",
      "createdAt": "2025-11-20T18:06:09.140Z",
      "updatedAt": "2025-11-20T18:06:09.140Z"
    },
    {
      "id": "S94PaeZJiPKn5Ei9",
      "name": "gmail",
      "createdAt": "2025-08-14T09:57:18.678Z",
      "updatedAt": "2025-08-14T09:57:18.678Z"
    },
    {
      "id": "Unbn35P90TtJuAQC",
      "name": "email",
      "createdAt": "2025-08-14T09:57:13.394Z",
      "updatedAt": "2025-08-14T09:57:13.394Z"
    },
    {
      "id": "IUTR4oS3KHYPxOqH",
      "name": "Jira",
      "createdAt": "2025-11-14T09:45:17.977Z",
      "updatedAt": "2025-11-14T09:45:17.977Z"
    },
    {
      "id": "SenQnwINjVwewgLY",
      "name": "slack",
      "createdAt": "2025-11-20T18:05:28.955Z",
      "updatedAt": "2025-11-20T18:05:28.955Z"
    },
    {
      "id": "qCgJLisgQ1EsyJ62",
      "name": "sql",
      "createdAt": "2025-11-20T18:05:47.931Z",
      "updatedAt": "2025-11-20T18:05:47.931Z"
    }
  ],
  "nodes": [
    {
      "id": "cb4115ac-4f5f-4939-ba38-31439073c734",
      "name": "Product anomalies",
      "type": "n8n-nodes-base.postgres",
      "position": [
        784,
        -272
      ],
      "parameters": {
        "query": "SELECT *\nFROM {{ $json[\"Source\"] }}\nWHERE {{ $json[\"Rule\"] }};\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "4bb4dbab-6476-4bc8-9db6-2e9449d72e54",
      "name": "check orders",
      "type": "n8n-nodes-base.postgres",
      "position": [
        768,
        176
      ],
      "parameters": {
        "query": "SELECT \n  'orders' AS table_name,\n  id,\n  customer_id,\n  total_amount,\n  status,\n  created_at\nFROM orders\nWHERE \n    total_amount IS NULL\n    OR total_amount = 0\n    OR customer_id IS NULL\n    OR id IS NULL;\n",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.6
    },
    {
      "id": "de7d4b56-045c-4d80-8405-c0935e19b6af",
      "name": "build summary",
      "type": "n8n-nodes-base.code",
      "position": [
        976,
        -48
      ],
      "parameters": {
        "jsCode": "const items = $input.all().map(i => i.json);\n\nconst anomalies = items;\nconst totalIssues = anomalies.length;\nconst totalRows = anomalies.length; \n\nconst score = totalIssues === 0\n  ? 100\n  : Math.max(0, 100 - Math.round((totalIssues / (totalRows || 1)) * 100));\n\nlet summary = `\ud83d\udcca Data Quality Report\\n\\n`;\nsummary += `Anomalies found: ${totalIssues}\\n`;\nsummary += `Quality score: ${score}/100\\n\\n`;\n\nanomalies.slice(0, 5).forEach((a, i) => {\n  summary += `${i + 1}. id=${a.id ?? \"N/A\"} | sku=${a.sku ?? \"N/A\"} | price=${a.price ?? \"N/A\"}\\n`;\n});\n\n\nreturn [\n  {\n    json: {\n      anomalies_found: totalIssues,\n      totalIssues,\n      totalRows,\n      qualityScore: score,\n      summary,          \n      summary_text: summary,\n      anomalies\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "5aa13ccf-c5d3-4a59-82b6-67e294274ebe",
      "name": "run metadata",
      "type": "n8n-nodes-base.set",
      "position": [
        1184,
        -64
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "aa5adb4a-13e2-495f-b215-548f850630e9",
              "name": "run_date",
              "type": "string",
              "value": "={{$now}}"
            },
            {
              "id": "b465d747-885f-4788-ad4b-08761eb7b933",
              "name": "run_id",
              "type": "number",
              "value": "={{$now}}-{{$randomNumber(1000,9999)}}"
            },
            {
              "id": "59aacd09-0863-4c35-ac1f-e3ca3b9f63f6",
              "name": "env",
              "type": "string",
              "value": "\"prod\""
            },
            {
              "id": "1af82ee3-2363-4017-aec4-afddb4a0b1c8",
              "name": "total_anomalies",
              "type": "number",
              "value": "={{$json[\"totalIssues\"]}}"
            },
            {
              "id": "b36da29e-d0b5-410a-8f7b-921e6cdb2a10",
              "name": "quality_score",
              "type": "number",
              "value": "={{$json[\"qualityScore\"]}}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "649e1b53-f8c9-4ae1-9967-80979a12a075",
      "name": "Data quality run page",
      "type": "n8n-nodes-base.notion",
      "position": [
        2592,
        -48
      ],
      "parameters": {
        "title": "Data Quality Incidents",
        "pageId": {
          "__rl": true,
          "mode": "url",
          "value": "https://www.notion.so"
        },
        "blockUi": {
          "blockValues": [
            {
              "type": "heading_1",
              "textContent": "Data quality {{date}}"
            },
            {
              "textContent": "=Title = DQ Run - {{$json[\"run_date\"]}}\n\nRun Date = {{$json[\"run_date\"]}}\n\nEnv = {{$json[\"env\"]}}\n\nTotal anomalies = {{$json[\"total_anomalies\"]}}\n\nQuality score = {{$json[\"quality_score\"]}}"
            }
          ]
        },
        "options": {}
      },
      "credentials": {
        "notionApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "29bfbca8-5a53-4c42-8960-025de7b0b404",
      "name": "AI analysis and recommendation",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        1120,
        256
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "GPT-4O-MINI"
        },
        "options": {},
        "responses": {
          "values": [
            {
              "content": "=You are a DataOps expert.\n\nHere is a data quality report:\n\n{{ $json[\"summary\"] }}\n\nReturn JSON only:\n\n{\n \"Summary\": \"...\",\n \"Urgency\": \"High|Medium|Low\",\n \"Root_Causes\": [\"...\", \"...\", \"...\"],\n \"Recommended_Fix\": [\"...\", \"...\", \"...\"],\n \"Impact\": \"High|Medium|Low\"\n}\n"
            }
          ]
        },
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "0aef1ab7-cabf-4e77-819e-20d796d27549",
      "name": "calculate trend",
      "type": "n8n-nodes-base.code",
      "position": [
        1408,
        -64
      ],
      "parameters": {
        "jsCode": "const current = $json.qualityScore ?? $json.quality_score ?? 100;\n$json.trend = 0;\nreturn [{ json: $json }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "f2f82d8c-7cac-4828-a721-3885a734ad7f",
      "name": "Autofix simulation",
      "type": "n8n-nodes-base.code",
      "position": [
        2192,
        -272
      ],
      "parameters": {
        "jsCode": "const rows = $json[\"anomalies\"] ?? [];\nrows.forEach(r => r.auto_fix = \"recommended\");\nreturn [{json:$json}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "6b7b101e-27fe-46dd-b9e7-e69be42777ed",
      "name": "split batch",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        320,
        0
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "176f8964-21c2-4164-8bc6-edb1cd15ab47",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -720,
        -448
      ],
      "parameters": {
        "width": 656,
        "height": 880,
        "content": "##  How it works\n\nThis workflow monitors data quality by running rule-based checks against your datasets and automatically alerting the team only when real anomalies are found \u2014 no noise, no manual review.\n\nEvery run begins by loading the Data Quality Rules from Notion, where each rule defines a dataset, a field, and a validation condition (e.g. price \u2264 0, missing ID, NULL amount). Rules are processed one-by-one and dynamically converted into live SQL queries, letting you scale checks without editing the workflow.\n\nQuery results are aggregated into a global summary, producing metrics such as total anomalies, severity, affected tables, and example rows. An AI node then generates a structured diagnostic: top issues, suspected root causes, impact assessment and recommended fixes.\n\n- If no anomaly is detected, the workflow ends quietly, clean runs remain silent and clutter-free.\n\n- If anomalies are detected, the workflow escalates. A Data Quality Run Page is created in Notion with full run metadata, trend score, and AI analysis. Depending on severity thresholds, the workflow can:\n\n- Send alerts to Slack or email\n\n- Create a Jira ticket for investigation\n\n- Tag anomalies with auto-fix suggestions\n\n- Log the incident for future audits\n\n- All runs are stored for long-term quality tracking, enabling dashboards, trend analysis, and continuous reliability improvement.\n\n## Setup steps\n\n- Create two Notion databases for quality runs and quality rules\n- Configure SQL database credentials \n- Add your OpenAI API credentials\n- Set up alert channels\n"
      },
      "typeVersion": 1
    },
    {
      "id": "9932aabc-8c8e-4a09-b47c-576ff173d297",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -48,
        -448
      ],
      "parameters": {
        "color": 7,
        "width": 1008,
        "height": 880,
        "content": "## 1) Data Quality Rules\n\nRuns configurable validation rules from Notion and executes SQL checks automatically."
      },
      "typeVersion": 1
    },
    {
      "id": "2c2b0c38-7b29-4c73-b3ae-abc30601f771",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        976,
        -448
      ],
      "parameters": {
        "color": 7,
        "width": 1056,
        "height": 880,
        "content": "## AI Summary & Scoring\n\nAggregates anomalies, generates a quality score, and provides root cause insights + recommended fixes."
      },
      "typeVersion": 1
    },
    {
      "id": "a056e103-e7a4-4a5b-b8b9-7da8eedc311a",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2048,
        -448
      ],
      "parameters": {
        "color": 7,
        "width": 1008,
        "height": 880,
        "content": "## 3) Incident Reporting & Alerts\n\nCreates Notion incident page, triggers Slack/Email/Jira alerts based on severity thresholds."
      },
      "typeVersion": 1
    },
    {
      "id": "5ef1e354-0eec-49ca-b7d0-dc700c4fb2d3",
      "name": "Daily trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        0
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 7
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "03cbc9dc-7bb4-4874-8434-65506099cacd",
      "name": "Get database data",
      "type": "n8n-nodes-base.notion",
      "position": [
        160,
        0
      ],
      "parameters": {
        "resource": "database",
        "databaseId": {
          "__rl": true,
          "mode": "list",
          "value": "2b711ca2-096c-80d2-8b0c-c86fa3a0c76f",
          "cachedResultUrl": "https://www.notion.so",
          "cachedResultName": "Data quality rules"
        }
      },
      "credentials": {
        "notionApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "0132534e-b672-4171-aaa1-9bddeecb967f",
      "name": "Switch module",
      "type": "n8n-nodes-base.switch",
      "position": [
        560,
        16
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "aa22f58e-2fd8-4ae4-a722-fe17cdf60152",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "Source",
                    "rightValue": "products"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "1239bf99-a9bd-4405-b380-7e6306c9c259",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "Source",
                    "rightValue": "orders"
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.3
    },
    {
      "id": "b2fcd298-b15d-4848-98e7-d620525d4063",
      "name": "Issue condition",
      "type": "n8n-nodes-base.if",
      "position": [
        1632,
        -64
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "0b1fe073-4a47-4b3b-981b-380194743c47",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json[\"anomalies_found\"] }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "bf35311a-0c9f-4f28-9302-aa36bc10cc1b",
      "name": "Data condition",
      "type": "n8n-nodes-base.if",
      "position": [
        1824,
        -224
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "c5ea8aaf-544d-416b-82a7-e44ff97878dc",
              "operator": {
                "type": "number",
                "operation": "lt"
              },
              "leftValue": "={{ $json[\"quality_score\"] }}",
              "rightValue": 80
            },
            {
              "id": "16258b64-8979-4d4c-a384-617af6a22d4c",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json[\"total_anomalies\"] }}",
              "rightValue": 50
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "bd7ea0ff-72d8-4efa-af0e-9b1609761c39",
      "name": "Jira issue",
      "type": "n8n-nodes-base.jira",
      "position": [
        2432,
        -288
      ],
      "parameters": {
        "project": {
          "__rl": true,
          "mode": "list",
          "value": "10002",
          "cachedResultName": "test2"
        },
        "summary": "=Data Quality Incident \u2013 {{$json[\"run_date\"]}}",
        "issueType": {
          "__rl": true,
          "mode": "list",
          "value": "10013",
          "cachedResultName": "Bug"
        },
        "additionalFields": {
          "priority": {
            "__rl": true,
            "mode": "list",
            "value": "2",
            "cachedResultName": "High"
          },
          "description": "={{$json[\"summary\"]}}"
        }
      },
      "credentials": {
        "jiraSoftwareCloudApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "83fdb751-de5d-47dc-944e-8a981dfbb21a",
      "name": "Slack message alert",
      "type": "n8n-nodes-base.slack",
      "position": [
        2784,
        -368
      ],
      "parameters": {
        "text": "=:rotating_light: *Data Quality Alert* ({{ $json[\"env\"] }}) \u2013 {{ $json[\"run_date\"] }}\n\n\u2022 Total anomalies : {{ $json[\"total_anomalies\"] }}\n\u2022 Quality score : {{ $json[\"quality_score\"] }}/100\n\nSumup :\n{{ $json[\"summary\"] }}\n\nplease check asap. :eyes:\n",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C0A030UC0BW",
          "cachedResultName": "data"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "e665cffc-44f5-4483-91bc-8cbb4be8095a",
      "name": "Email reporting",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2800,
        224
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=Hi Data Team,  please find below the data quality report :  <pre>{{ $json[\"summary_text\"] }}</pre>  an incident has been \u00e9created in Notion : - Link : \nHave a great day",
        "options": {},
        "subject": "=Data Quality Report \u2013 {{date}}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "61ca3db1-d2be-4869-8d98-1567c9fdd0e0",
  "connections": {
    "Jira issue": {
      "main": [
        [
          {
            "node": "Data quality run page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "split batch": {
      "main": [
        [],
        [
          {
            "node": "Switch module",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "check orders": {
      "main": [
        [
          {
            "node": "build summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "run metadata": {
      "main": [
        [
          {
            "node": "calculate trend",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily trigger": {
      "main": [
        [
          {
            "node": "Get database data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch module": {
      "main": [
        [
          {
            "node": "Product anomalies",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "check orders",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "build summary": {
      "main": [
        [
          {
            "node": "AI analysis and recommendation",
            "type": "main",
            "index": 0
          },
          {
            "node": "run metadata",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data condition": {
      "main": [
        [
          {
            "node": "Autofix simulation",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Data quality run page",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Issue condition": {
      "main": [
        [
          {
            "node": "Data condition",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "calculate trend": {
      "main": [
        [
          {
            "node": "Issue condition",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get database data": {
      "main": [
        [
          {
            "node": "split batch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Product anomalies": {
      "main": [
        [
          {
            "node": "build summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Autofix simulation": {
      "main": [
        [
          {
            "node": "Jira issue",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack message alert": {
      "main": [
        []
      ]
    },
    "Data quality run page": {
      "main": [
        [
          {
            "node": "Email reporting",
            "type": "main",
            "index": 0
          },
          {
            "node": "Slack message alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI analysis and recommendation": {
      "main": [
        [
          {
            "node": "calculate trend",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}