AutomationFlowsSlack & Telegram › Track Certification Requirements with Scrapegraphai, Gitlab and Rocket.chat

Track Certification Requirements with Scrapegraphai, Gitlab and Rocket.chat

Byvinci-king-01 @vinci-king-01 on n8n.io

⚠️ COMMUNITY TEMPLATE DISCLAIMER: This is a community-contributed template that uses ScrapeGraphAI (a community node). Please ensure you have the ScrapeGraphAI community node installed in your n8n instance before using this template.

Webhook trigger★★★★☆ complexity18 nodesN8N Nodes ScrapegraphaiGitLabRocketchat
Slack & Telegram Trigger: Webhook Nodes: 18 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #12234 — we link there as the canonical source.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "y0Yk7da21T4u9zlp",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Certification Requirement Tracker with Rocket.Chat and GitLab",
  "tags": [],
  "nodes": [
    {
      "id": "60a587ac-1ae6-42d3-ba21-ae3bbdadc9ec",
      "name": "Workflow Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -208,
        368
      ],
      "parameters": {
        "width": 550,
        "height": 738,
        "content": "## How it works\n\nThis workflow lets certification-holding professionals subscribe to live updates from the bodies that publish renewal rules. A webhook call\u2014ideal for a button in your portal or a scheduled external ping\u2014fires the flow. The Code node supplies a curated list of certification-body URLs. ScrapeGraphAI then pulls the latest requirement text, effective date and version from each site. After a quick normalisation pass the results are bundled into one JSON payload and compared to the last stored copy in a GitLab repo. If even a single requirement changed, the IF node steers execution down the alert path: a Rocket.Chat message posts the diff and GitLab receives a commit containing the new canonical file. If nothing changed, the workflow simply answers the webhook with a 200 and the current data.\n\n## Setup steps\n\n1. Add ScrapeGraphAI credentials in n8n\n2. Fill the Code node with your certification URLs\n3. Create or select a GitLab project and set repo access credentials\n4. Configure a file path and branch in both GitLab nodes\n5. Add Rocket.Chat API credentials and channel name\n6. Deploy the webhook URL where your scheduler can reach it\n7. Enable the workflow and trigger a test run"
      },
      "typeVersion": 1
    },
    {
      "id": "04b7e6a7-f8c9-49fe-a75c-2848ea758ac2",
      "name": "Section \u2013 Trigger & Config",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        480,
        352
      ],
      "parameters": {
        "color": 7,
        "width": 450,
        "height": 670,
        "content": "## Trigger & URL Configuration\n\nThe three nodes beneath this note collectively kick-off the entire automation. The **Incoming Webhook** waits for any POST call\u2014this keeps the workflow decoupled from specific schedulers or front-end apps. Immediately after, the **Prepare Certification URLs** Code node outputs one item per website you want to monitor. Keeping the list in code lets you version-control URLs and dynamically alter them without editing every downstream node. Because each item travels independently in n8n, a dedicated **item** is produced for every certification authority, making the subsequent ScrapeGraphAI execution perfectly parallel-friendly. Finally, no API keys appear here; the only configuration you need is to paste your target URLs and perhaps add headers if individual sites require special cookies or tokens."
      },
      "typeVersion": 1
    },
    {
      "id": "69c51aa4-d3d6-4235-ba9d-a38c07ae338b",
      "name": "Section \u2013 Scraping",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        960,
        352
      ],
      "parameters": {
        "color": 7,
        "width": 498,
        "height": 654,
        "content": "## AI-Powered Scraping\n\nScrapeGraphAI sits at the heart of this section. For every incoming URL it receives from the previous step, the node uses its LLM-backed engine to locate and extract a **certification name**, a **requirement text**, and the **last-updated date**. The natural-language prompt instructs the AI to respond in a strict JSON schema so the workflow never breaks if the HTML layout shifts. Immediately downstream, the **Normalize Requirement Data** Code node polishes field names, converts dates to ISO-8601, and adds a hash we later use for quick diffing. This means any cosmetic spacing change in the requirement text will be detected as an actual update, guaranteeing that subscribers are never left with stale information."
      },
      "typeVersion": 1
    },
    {
      "id": "43bff25b-c2d2-467f-98c9-80d17f3e867d",
      "name": "Section \u2013 Aggregation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1456,
        352
      ],
      "parameters": {
        "color": 7,
        "width": 450,
        "height": 654,
        "content": "## Aggregation & Baseline Retrieval\n\nOnce each requirement item is clean, the **Aggregate Results** Code node gathers all individual items into a single JSON array named `currentRequirements`. This ensures later comparison logic only has to deal with one record. Right after aggregation, a GitLab node fetches `requirements.json` from your repository\u2014this is the baseline the workflow compares against. Both the freshly scraped data and the stored baseline feed into a Merge node so that the next Code node can access them side-by-side. Fetching from GitLab keeps a complete audit history in your source-control system, making regulatory compliance and rollbacks effortless."
      },
      "typeVersion": 1
    },
    {
      "id": "feb1182d-f752-45f1-b648-339fdfd9fd57",
      "name": "Section \u2013 Decision & Alerting",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1968,
        336
      ],
      "parameters": {
        "color": 7,
        "width": 450,
        "height": 670,
        "content": "## Change Detection & Routing\n\nThe **Detect Changes** Code node receives both the freshly scraped array and the baseline file contents. It performs a deep comparison by stringifying each object and checking for mismatches. A boolean flag `changed` plus a trimmed `diff` object travel forward. The **Requirements Changed?** IF node then provides the mandatory conditional branching pattern: `true` leads to alerting and file update, `false` short-circuits directly to the webhook response. Structuring logic this way prevents unnecessary GitLab commits and Rocket.Chat noise, saving API resources and avoiding alert fatigue for your users."
      },
      "typeVersion": 1
    },
    {
      "id": "c7ef8b21-f99b-4b9a-b6bc-0b4f1e88816b",
      "name": "Section \u2013 Storage & Notification",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2448,
        352
      ],
      "parameters": {
        "color": 7,
        "width": 450,
        "height": 670,
        "content": "## Storage & Notifications\n\nWhen the IF node flags a change, two things happen in parallel. First, a **Rocket.Chat** node publishes a concise message\u2014along with a JSON code-block diff\u2014directly to the channel you specified, ensuring certification managers see critical updates instantly. Second, the **Update GitLab Record** node commits the new `requirements.json` to your repo with a clear commit message so the change becomes part of your long-term audit trail. Both branches eventually rejoin at **Respond to Webhook**, which returns a 200 status plus a summary object. Even if no changes occurred, the workflow still answers cleanly, allowing external schedulers to confirm successful execution."
      },
      "typeVersion": 1
    },
    {
      "id": "0513e5d7-9756-48ad-a62f-1ca96f29285f",
      "name": "Incoming Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        512,
        704
      ],
      "parameters": {
        "path": "certification-requirements",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 1
    },
    {
      "id": "7a60284c-f53e-428f-a783-197989db5929",
      "name": "Prepare Certification URLs",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        704
      ],
      "parameters": {
        "jsCode": "/*\n * Return one item per certification-body URL.\n * Replace the sample URLs with the real pages you want to monitor.\n */\nreturn [\n  { json: { url: 'https://example-certbody1.org/requirements' } },\n  { json: { url: 'https://example-association2.com/certification-updates' } }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "1c1701a3-156b-432a-b62e-4c3dcf4eff88",
      "name": "Scrape Certification Requirements",
      "type": "n8n-nodes-scrapegraphai.scrapegraphAi",
      "position": [
        1008,
        704
      ],
      "parameters": {
        "userPrompt": "Extract the certification name, the full requirement text, and the last updated date from the provided page. Respond strictly in this JSON schema: {\"certification_name\":\"string\",\"requirement_text\":\"string\",\"last_updated\":\"ISO8601\"}",
        "websiteUrl": "={{ $json.url }}"
      },
      "credentials": {
        "scrapegraphAIApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7e4f845c-2609-4021-8455-c6326bc38716",
      "name": "Normalize Requirement Data",
      "type": "n8n-nodes-base.code",
      "position": [
        1168,
        704
      ],
      "parameters": {
        "jsCode": "/*\n * Normalise and enrich each scraped item.\n * Adds a hash for quick diffing.\n */\nconst crypto = require('crypto');\nreturn $input.all().map(item => {\n  const data = item.json;\n  const hash = crypto.createHash('sha256').update(data.certification_name + data.requirement_text).digest('hex');\n  return {\n    json: {\n      certificationName: data.certification_name,\n      requirementText: data.requirement_text,\n      lastUpdated: new Date(data.last_updated).toISOString(),\n      hash\n    }\n  };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "3fb1f1bf-2778-4f4e-a9e3-38205f0ae48b",
      "name": "Aggregate Results",
      "type": "n8n-nodes-base.code",
      "position": [
        1312,
        704
      ],
      "parameters": {
        "jsCode": "// Aggregate all items into one object for easy comparison\nconst items = $input.all();\nreturn [\n  {\n    json: {\n      currentRequirements: items.map(i => i.json)\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "71659c6d-1f11-49d9-8bd6-f53d99737970",
      "name": "Get Baseline From GitLab",
      "type": "n8n-nodes-base.gitlab",
      "position": [
        1520,
        656
      ],
      "parameters": {
        "resource": "repositoryFile"
      },
      "typeVersion": 1
    },
    {
      "id": "de304529-ebb4-4c1d-a7da-226e2a830384",
      "name": "Merge Current & Baseline",
      "type": "n8n-nodes-base.merge",
      "position": [
        1520,
        864
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "mergeByFields": {
          "values": [
            {}
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "4dc02ba4-560e-4005-ac25-0bf7b17729b4",
      "name": "Detect Changes",
      "type": "n8n-nodes-base.code",
      "position": [
        1728,
        800
      ],
      "parameters": {
        "jsCode": "/*\n * Input[0] = current data, Input[1] = baseline file data.\n */\nconst [currentItem, baselineItem] = $input.all();\nconst currentReq = currentItem.json.currentRequirements;\nlet baselineReq = [];\ntry {\n  if (baselineItem.json.content) {\n    baselineReq = JSON.parse(Buffer.from(baselineItem.json.content, 'base64').toString());\n  }\n} catch (e) {\n  baselineReq = [];\n}\nconst changed = JSON.stringify(currentReq) !== JSON.stringify(baselineReq);\nreturn [{\n  json: {\n    changed,\n    diff: changed ? currentReq : [],\n    currentRequirements: currentReq\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "63ea089a-f653-4d20-80f5-840a1a093344",
      "name": "Requirements Changed?",
      "type": "n8n-nodes-base.if",
      "position": [
        2016,
        816
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.changed }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "ad46c00e-8c17-44b2-ab37-b2332a0c1b43",
      "name": "Send Rocket.Chat Alert",
      "type": "n8n-nodes-base.rocketchat",
      "position": [
        2464,
        672
      ],
      "parameters": {
        "text": "={{ 'Certification requirements updated. See GitLab for full details. Changed items: ' + JSON.stringify($json.diff, null, 2) }}",
        "channel": "#cert-alerts",
        "options": {},
        "attachments": []
      },
      "typeVersion": 1
    },
    {
      "id": "fb77fc49-d0e7-4339-bb8e-65f41f1ad432",
      "name": "Update GitLab Record",
      "type": "n8n-nodes-base.gitlab",
      "position": [
        2224,
        880
      ],
      "parameters": {
        "resource": "repositoryFile"
      },
      "typeVersion": 1
    },
    {
      "id": "f02b6222-b38f-4db5-b7f6-56c81ae45270",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2640,
        816
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "df702c16-7920-4841-8ee0-474ec13e8d9a",
  "connections": {
    "Detect Changes": {
      "main": [
        [
          {
            "node": "Requirements Changed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Incoming Webhook": {
      "main": [
        [
          {
            "node": "Prepare Certification URLs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Results": {
      "main": [
        [
          {
            "node": "Get Baseline From GitLab",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge Current & Baseline",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Update GitLab Record": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Requirements Changed?": {
      "main": [
        [
          {
            "node": "Send Rocket.Chat Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Update GitLab Record",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Rocket.Chat Alert": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Baseline From GitLab": {
      "main": [
        [
          {
            "node": "Merge Current & Baseline",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Current & Baseline": {
      "main": [
        [
          {
            "node": "Detect Changes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Requirement Data": {
      "main": [
        [
          {
            "node": "Aggregate Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Certification URLs": {
      "main": [
        [
          {
            "node": "Scrape Certification Requirements",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Certification Requirements": {
      "main": [
        [
          {
            "node": "Normalize Requirement Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

⚠️ COMMUNITY TEMPLATE DISCLAIMER: This is a community-contributed template that uses ScrapeGraphAI (a community node). Please ensure you have the ScrapeGraphAI community node installed in your n8n instance before using this template.

Source: https://n8n.io/workflows/12234/ — original creator credit. Request a take-down →

More Slack & Telegram workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Slack & Telegram

⚠️ COMMUNITY TEMPLATE DISCLAIMER: This is a community-contributed template that uses ScrapeGraphAI (a community node). Please ensure you have the ScrapeGraphAI community node installed in your n8n ins

N8N Nodes Scrapegraphai, GitLab, Slack
Slack & Telegram

⚠️ COMMUNITY TEMPLATE DISCLAIMER: This is a community-contributed template that uses ScrapeGraphAI (a community node). Please ensure you have the ScrapeGraphAI community node installed in your n8n ins

N8N Nodes Scrapegraphai, Slack, Jira
Slack & Telegram

Cryptocurrency traders and investors DeFi protocol managers and developers Blockchain security analysts Financial compliance officers Crypto fund managers and institutions Risk management teams Blockc

N8N Nodes Scrapegraphai, Slack
Slack & Telegram

HR teams, IT Operations, and System Administrators managing employee onboarding at scale. It’s perfect if you use Odoo 18 to trigger account requests and need Redmine + GitLab accounts created instant

HTTP Request, Slack
Slack & Telegram

This workflow is a complete, production-ready solution for recovering abandoned carts in Shopify stores using a multi-channel, multi-touch approach. It automates personalized follow-ups via Email, SMS

HTTP Request, Shopify, SendGrid +5