Updated Folder
This commit is contained in:
1 parent
a3dfb5d293
commit
d6462d6721
53 files changed
+22535
-1
No files matched your search
+74
@@ -0,0 +1,74 @@
|
||||
***Tags**: Supply Chain, Inventory Management, ABC Analysis, Pareto Principle, Demand Variability, Automation, Google Sheets*
|
||||
|
||||
### Context
|
||||
|
||||
Hi! I’m Samir — a Supply Chain Engineer and Data Scientist based in Paris, and founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
I help companies **optimise inventory and logistics operations** by combining **data analytics** and **workflow automation**.
|
||||
|
||||
This workflow is part of our **inventory optimisation toolkit**, allowing businesses to perform **ABC classification and Pareto analysis** directly from their transactional sales data.
|
||||
|
||||
> Automate inventory segmentation with n8n!
|
||||
|
||||
📬 For business inquiries, feel free to connect with me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow is designed for **supply chain analysts**, **demand planners**, or **inventory managers** who want to:
|
||||
|
||||
- Identify their top-performing items (Pareto 80/20 principle)
|
||||
- Classify products into **ABC categories** based on sales contribution
|
||||
- Evaluate demand variability (XYZ classification support)
|
||||
|
||||
Imagine you have a **Google Sheet** where daily sales transactions are stored:
|
||||
|
||||

|
||||
|
||||
The workflow aggregates sales by item, calculates cumulative contribution, and assigns **A, B, or C** classes. It also computes **mean, standard deviation, and coefficient of variation (CV)** to highlight demand volatility.
|
||||
|
||||

|
||||
|
||||
### How does it work?
|
||||
|
||||
This workflow automates the process of **ABC & Pareto analysis from raw sales data**:
|
||||
|
||||
- 📊 Google Sheets input provides daily transactional sales
|
||||
- 🧮 Aggregation & code nodes compute sales, turnover, and cumulative shares
|
||||
- 🧠 ABC class mapping assigns items into A/B/C buckets
|
||||
- 📈 Demand variability metrics (XYZ) are calculated
|
||||
- 📑 Results are appended into dedicated Google Sheets tabs for reporting
|
||||
|
||||
[](https://www.youtube.com/watch?v=YbAA-cq9X_E)
|
||||
|
||||
🎥 [Watch My Tutorial](https://www.youtube.com/watch?v=YbAA-cq9X_E)
|
||||
|
||||
### Steps:
|
||||
|
||||
1. 📝 Load daily sales records from Google Sheets
|
||||
2. 🔎 Filter out items with zero sales
|
||||
3. 📊 Aggregate sales by **store**, **item**, and **day**
|
||||
4. 📈 Perform Pareto analysis to calculate cumulative turnover share
|
||||
5. 🧮 Compute demand variability (mean, stdev, CV)
|
||||
6. 🧠 Assign ABC classes based on cumulative share thresholds
|
||||
7. 📥 Append results into **ABC XYZ** and **Pareto** output sheets
|
||||
|
||||

|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
You’ll need:
|
||||
|
||||
- A **Google Sheet** with sales transactions (date, item, quantity, turnover) that is available here: [Test Sheet](https://docs.google.com/spreadsheets/d/1nsS_pj3o8yMEIQ59QBU80sx20HtkkTgCKJB32sOge1o/edit?usp=sharing)
|
||||
- A **Google Sheets account** connected in n8n
|
||||
- Basic knowledge of inventory analysis (ABC/XYZ)
|
||||
|
||||
### Next Steps
|
||||
|
||||
🗒️ Use the sticky notes in the n8n canvas to:
|
||||
|
||||
- Add your Google Sheets credentials
|
||||
- Replace the Sheet ID with your own sales dataset
|
||||
- Run the workflow and check the output tabs: **ABC XYZ**, **Pareto**, and **Store Sales**
|
||||
|
||||
*This template was built using n8n v1.107.3*
|
||||
*Submitted: September 15, 2025*
|
||||
+882
@@ -0,0 +1,882 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": "",
|
||||
"templateCredsSetupCompleted": true
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "dd09796e-b4c4-43df-8401-bba895d330b3",
|
||||
"name": "When clicking ‘Execute workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"position": [
|
||||
-1056,
|
||||
-400
|
||||
],
|
||||
"parameters": {},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "5f502423-b87c-4322-aac6-10d07e685087",
|
||||
"name": "Get row(s) in sheet",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
-832,
|
||||
-400
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Input Data"
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Data Analytics"
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.7
|
||||
},
|
||||
{
|
||||
"id": "15e8b11a-83d9-41cb-84e4-dbfe0ddf970f",
|
||||
"name": "Filter Out Zero Sales",
|
||||
"type": "n8n-nodes-base.filter",
|
||||
"position": [
|
||||
-608,
|
||||
-400
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 2,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "39ce1523-ed7e-404a-82db-099200bcb82c",
|
||||
"operator": {
|
||||
"type": "number",
|
||||
"operation": "notEquals"
|
||||
},
|
||||
"leftValue": "={{ $json.QTY }}",
|
||||
"rightValue": "={{ 0 }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 2.2
|
||||
},
|
||||
{
|
||||
"id": "2cd9b9a0-dc4f-4d99-b4c4-43f6d165cad8",
|
||||
"name": "Transpose",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
-160,
|
||||
-592
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "const input = $input.all().map(i => i.json);\n\n// Collect store IDs\nconst stores = input.map(row => row.STORE);\n// Collect items (from keys)\nconst items = Object.keys(input[0]).filter(k => k !== \"STORE\");\n\n// Transpose\nconst result = items.map(ITEM => {\n let row = { ITEM };\n for (const store of stores) {\n const storeRow = input.find(r => r.STORE === store);\n row[store] = storeRow[ITEM];\n }\n return { json: row };\n});\n\nreturn result;\n"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "7bedeb08-f47e-4809-9936-7cdc8a9236d1",
|
||||
"name": "Daily Sales per Store",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
-384,
|
||||
-304
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "const agg = {};\nfor (const {json} of items) {\n const k = `${json.STORE}|${json.DAY}`;\n if (!agg[k]) agg[k] = { STORE: json.STORE, DAY: json.DAY, QTY: 0, TO: 0 };\n agg[k].QTY += Number(json.QTY||0);\n agg[k].TO += Number(json.TO||0);\n}\nreturn Object.values(agg).map(r => ({ json: r }));"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "b0134581-ef21-4485-bd75-8a5d7f865ad4",
|
||||
"name": "If",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"position": [
|
||||
-160,
|
||||
-304
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 2,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "59bb6e5b-0765-4812-a842-19a3bc7a0357",
|
||||
"operator": {
|
||||
"name": "filter.operator.equals",
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
},
|
||||
"leftValue": "={{ $json.STORE }}",
|
||||
"rightValue": "STORE-1"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 2.2
|
||||
},
|
||||
{
|
||||
"id": "95a2efc7-9c7b-4263-aab4-c00bbe902714",
|
||||
"name": "Sales Store 1",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
64,
|
||||
-400
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"TO": "={{ $json.TO }}",
|
||||
"DAY": "={{ $json.DAY }}",
|
||||
"QTY": "={{ $json.QTY }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "DAY",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "DAY",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "QTY",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "QTY",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "TO",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "TO",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": 421687347,
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Daily Sales Store 1"
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Data Analytics"
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.7
|
||||
},
|
||||
{
|
||||
"id": "fdffd98a-36ac-425b-861c-b3f738828fe2",
|
||||
"name": "Sales Store 2",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
64,
|
||||
-208
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"TO": "={{ $json.TO }}",
|
||||
"DAY": "={{ $json.DAY }}",
|
||||
"QTY": "={{ $json.QTY }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "DAY",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "DAY",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "QTY",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "QTY",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "TO",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "TO",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": 749936247,
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Daily Sales Store 2"
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Data Analytics"
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.7
|
||||
},
|
||||
{
|
||||
"id": "8a3afe9e-d873-4a02-9fb1-45fc9c380601",
|
||||
"name": "Pareto Analysis",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
-160,
|
||||
-784
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "// Normalize + sort by turnover (TO) desc\nconst rows = items\n .map(i => ({\n ...i.json,\n TO: Number(i.json.TO || 0), // turnover = QTY x Unit Price\n QTY: Number(i.json.QTY || 0),\n }))\n .sort((a, b) => b.TO - a.TO);\n\nconst n = rows.length;\nconst totalTO = rows.reduce((s, r) => s + r.TO, 0) || 1;\n\nlet cumTO = 0;\nrows.forEach((r, idx) => {\n cumTO += r.TO;\n r.cum_turnover = cumTO; // absolute cumulative TO\n r.cum_share = +(cumTO / totalTO).toFixed(6); // 0..1\n r.sku_rank = idx + 1;\n r.cum_skus = +((idx + 1) / n).toFixed(6); // 0..1\n r.cum_skus_pct = +(r.cum_skus * 100).toFixed(2); // 0..100 for charts\n});\n\n// Return table\nreturn rows.map(r => ({ json: r }));\n\n"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "e8e1409d-b571-43b8-ae9e-bb8da228cefd",
|
||||
"name": "TO, QTY GroupBy ITEM",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
-384,
|
||||
-784
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "const agg = {};\nfor (const {json} of items) {\n const ITEM = String(json.ITEM);\n const TO = Number(json.TO);\n const QTY = Number(json.QTY);\n if (!agg[ITEM]) agg[ITEM] = { ITEM, TO: 0, QTY: 0 };\n agg[ITEM].TO += TO;\n agg[ITEM].QTY += QTY;\n}\nconst rows = Object.values(agg).sort((a,b)=> b.TO - a.TO);\nreturn rows.map(r => ({ json: r }));"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "9e02d5cb-701d-4a40-b2bf-c2fae43af8ef",
|
||||
"name": "TO GroupBy (STORE, ITEM)",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
-384,
|
||||
-592
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "// Build dictionaries of unique stores & skus\nconst stores = new Set();\nconst skus = new Set();\n// Append dictionnaries with values\nfor (const {json} of items) { stores.add(json.STORE); skus.add(json.ITEM); }\n// List of Unique SKU\nconst skuList = Array.from(skus).sort();\n\n// Accumulate sums\nconst mat = {}; // store_id -> sku -> sum\nfor (const {json} of items) {\n const s = json.STORE;\n const k = json.ITEM;\n const v = Number(json.TO) || 0;\n if (!mat[s]) mat[s] = {};\n mat[s][k] = (mat[s][k] || 0) + v;\n}\n\n// Output wide rows with 0 fill\nconst rows = [];\nfor (const s of Array.from(stores).sort()) {\n const row = { STORE: s };\n for (const k of skuList) row[k] = mat[s][k] || 0;\n rows.push(row);\n}\nreturn rows.map(r => ({ json: r }));\n"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "efe6f052-ff5d-4755-ae07-4a7bc480ddbb",
|
||||
"name": "Multi-Store Sales",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
64,
|
||||
-592
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"TO": "={{ $json.TO }}",
|
||||
"DAY": "={{ $json.DAY }}",
|
||||
"QTY": "={{ $json.QTY }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "DAY",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "DAY",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "QTY",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "QTY",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "TO",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "TO",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": 421687347,
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Daily Sales Store 1"
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Data Analytics"
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.7
|
||||
},
|
||||
{
|
||||
"id": "5bd88834-9c20-4d9e-bd31-146dc31e2c18",
|
||||
"name": "Demand Variability x Sales %",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
-384,
|
||||
-16
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "// INPUT items: rows with fields ITEM, DAY, QTY (others optional)\n\nfunction mean(a){ return a.reduce((s,x)=>s + x, 0) / (a.length || 1); }\nfunction stdev_samp(a){\n if (a.length <= 1) return 0;\n const m = mean(a);\n const v = a.reduce((s,x)=> s + (x - m) ** 2, 0) / (a.length - 1);\n return Math.sqrt(v);\n}\n\n// 1) build daily series per ITEM (sum across stores)\nconst series = {}; // ITEM -> { day -> qty_sum }\nlet totalQty = 0;\n\nfor (const { json } of items) {\n const item = String(json.ITEM);\n const day = String(json.DAY);\n const qty = Number(json.QTY || 0);\n\n if (!series[item]) series[item] = {};\n series[item][day] = (series[item][day] || 0) + qty;\n totalQty += qty;\n}\n\n// 2) compute metrics per ITEM\nconst out = [];\nfor (const [item, dayMap] of Object.entries(series)) {\n const daily = Object.values(dayMap); // array of daily qty\n const qty_total = daily.reduce((s,x)=>s+x, 0);\n const m = mean(daily);\n const sd = stdev_samp(daily);\n const cv = m ? sd / m : null;\n const share_qty_pct = totalQty ? (qty_total / totalQty) * 100 : 0;\n\n out.push({\n ITEM: item,\n qty_total,\n share_qty_pct: Number(share_qty_pct.toFixed(2)),\n mean_qty: Number(m.toFixed(3)),\n std_qty: Number(sd.toFixed(3)),\n cv_qty: cv == null ? null : Number(cv.toFixed(3)),\n });\n}\n\n// Optional: sort by share desc\nout.sort((a,b) => b.share_qty_pct - a.share_qty_pct);\n\n// Return as n8n items\nreturn out.map(r => ({ json: r }));\n"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "805e6a80-bb24-446e-ab9c-a0e0c68f555a",
|
||||
"name": "ABC XYZ Analysis",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
64,
|
||||
-16
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"ABC": "={{ $json.ABC }}",
|
||||
"ITEM": "={{ $json.ITEM }}",
|
||||
"cv_qty": "={{ $json.cv_qty }}",
|
||||
"std_qty": "={{ $json.std_qty }}",
|
||||
"mean_qty": "={{ $json.mean_qty }}",
|
||||
"cum_share": "={{ $json.cum_share }}",
|
||||
"qty_total": "={{ $json.qty_total }}",
|
||||
"share_qty_pct": "={{ $json.share_qty_pct }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "ITEM",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "ITEM",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "qty_total",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "qty_total",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "share_qty_pct",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "share_qty_pct",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "cv_qty",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "cv_qty",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "ABC",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "ABC",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "mean_qty",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "mean_qty",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "std_qty",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "std_qty",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "cum_share",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "cum_share",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "limit_cov",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "limit_cov",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {
|
||||
"useAppend": false
|
||||
},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": 1281688243,
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "ABC XYZ"
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Data Analytics"
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.7
|
||||
},
|
||||
{
|
||||
"id": "fb1b23cb-cad9-4a75-bb41-5d87209b51c5",
|
||||
"name": "Update Pareto Sheet",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
64,
|
||||
-784
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"TO": "={{ $json.TO }}",
|
||||
"QTY": "={{ $json.QTY }}",
|
||||
"ITEM": "={{ $json.ITEM }}",
|
||||
"cum_skus": "={{ $json.cum_skus }}",
|
||||
"sku_rank": "={{ $json.sku_rank }}",
|
||||
"cum_share": "={{ $json.cum_share }}",
|
||||
"cum_skus_pct": "={{ $json.cum_skus_pct }}",
|
||||
"cum_turnover": "={{ $json.cum_turnover }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "ITEM",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "ITEM",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "TO",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "TO",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "QTY",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "QTY",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "cum_turnover",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "cum_turnover",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "cum_share",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "cum_share",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "sku_rank",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "sku_rank",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "cum_skus",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "cum_skus",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "cum_skus_pct",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "cum_skus_pct",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": 1760689869,
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Pareto"
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Data Analytics"
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.7
|
||||
},
|
||||
{
|
||||
"id": "82773b29-a1dc-445c-a8ea-d9c393d36eb4",
|
||||
"name": "ABC Class Mapping",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
-160,
|
||||
-16
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "// 1) Extract and sort by sales contribution\nlet rows = items.map(i => i.json);\nrows.sort((a, b) => b.share_qty_pct - a.share_qty_pct);\n\n// 2) Compute cumulative contribution\nlet cum = 0;\nfor (let r of rows) {\n cum += r.share_qty_pct;\n\n // 3) Assign class based on cumulative %\n if (cum <= 5) {\n r.ABC = 'A'; // top 5%\n } else if (cum <= 20) {\n r.ABC = 'B'; // next 15%\n } else {\n r.ABC = 'C'; // rest\n }\n\n r.cum_share = Number(cum.toFixed(2));\n}\n\n// 4) Return enriched rows\nreturn rows.map(r => ({ json: r }));\n"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "21867671-78aa-4580-b51f-53c34f6a1dfa",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-1166,
|
||||
-784
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 540,
|
||||
"height": 544,
|
||||
"content": "### Trigger the workflow\nThis starts by collecting the transactional sales from the spreadsheet `Input Data` from the Google Sheet.\n\n#### How to setup?\n\n- **Load records in the Google Sheet Node**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet `Input Data`\nination**, **distance**, **duration**, **n_steps**\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"If": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Sales Store 1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Sales Store 2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Transpose": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Multi-Store Sales",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Pareto Analysis": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Update Pareto Sheet",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"ABC Class Mapping": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "ABC XYZ Analysis",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get row(s) in sheet": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Filter Out Zero Sales",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"TO, QTY GroupBy ITEM": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Pareto Analysis",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Daily Sales per Store": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "If",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Filter Out Zero Sales": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Demand Variability x Sales %",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "TO, QTY GroupBy ITEM",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "TO GroupBy (STORE, ITEM)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Daily Sales per Store",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"TO GroupBy (STORE, ITEM)": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Transpose",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Demand Variability x Sales %": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "ABC Class Mapping",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"When clicking ‘Execute workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get row(s) in sheet",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
Submodule n8n-workflows deleted from 929ff7c736.
+85
@@ -0,0 +1,85 @@
|
||||
***Tags**: AI Agent, Supply Chain, Logistics, Circular Economy, Route Planning, Transportation, GPS API*
|
||||
|
||||
### Context
|
||||
|
||||
Hi! I’m Samir — a Supply Chain Engineer and Data Scientist based in Paris, and founder of [LogiGreen Consulting
|
||||
](https: //logi-green.com).
|
||||
|
||||
I help logistics teams reduce operational workload and errors by combining **AI automation**, **route optimisation APIs**, and **workflow automation**.
|
||||
|
||||
This workflow is part of a **circular economy project**, where stores return **reusable packaging** (bins, crates, containers) to a central warehouse.
|
||||
|
||||
> Let's create circular economies with AI-powered automation using n8n!
|
||||
|
||||
📬 For business inquiries, you can find me on [LinkedIn
|
||||
](https: //www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow is designed for logistics teams participating in **circular economy loops**.
|
||||
|
||||
Let us imagine your transportation company receives this **pickup request**:
|
||||
|
||||

|
||||
|
||||
The two **AI Agent nodes** connected to **Openroute Service API** will process the information and reply with the **detailed route plan**.
|
||||
|
||||

|
||||
|
||||
The results include **driving time** and the **optimal sequence** of stops generated by the multi-stop optimization endpoint of the API.
|
||||
|
||||
### How does it work?
|
||||
|
||||
This workflow automates the end-to-end processing of **multi-stop pickup requests for reusable packaging**:
|
||||
|
||||
- 📨 Gmail Trigger listens for collection request emails
|
||||
- 🧠 AI Agent parses the email into structured data (store ID, address, date)
|
||||
- 📍 Each stop is geocoded into GPS coordinates
|
||||
- 🗺️ OpenRouteService **optimizes the stop sequence** using truck-specific routing
|
||||
- 📄 A second AI Agent formats a **confirmation email in HTML** with the ordered pickup plan
|
||||
- 📧 The reply is sent back with all details including duration and route
|
||||
|
||||
### Steps:
|
||||
|
||||
1. 💌 Trigger on a new Gmail message
|
||||
2. 🧠 Extract data using AI Agent (e.g., stores, addresses, times)
|
||||
3. 📑 Store raw and processed data in Google Sheets
|
||||
4. 📍 Enrich with GPS coordinates
|
||||
5. 🚚 Optimize route using OpenRouteService (truck profile)
|
||||
6. 📄 Format the confirmation using an AI Agent
|
||||
7. 📬 Send reply to requester with route and timing
|
||||
|
||||

|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
You’ll need:
|
||||
|
||||
- A **Gmail account** to receive collection requests
|
||||
- A **Google Sheet** to store and review data
|
||||
- A free [OpenRouteService API key
|
||||
](https: //openrouteservice.org/dev/#/signup)
|
||||
- Access to OpenAI for using AI Agent nodes
|
||||
- Sample pickup request emails to test
|
||||
|
||||
### Next Steps
|
||||
|
||||
🗒️ Use the sticky notes inside the n8n canvas to:
|
||||
|
||||
- Plug in your Gmail and OpenRouteService credentials
|
||||
- Try with a sample store collection email
|
||||
- Validate the confirmation format and route accuracy
|
||||
|
||||
### 📺 Learn More with n8n Tutorials
|
||||
|
||||
Get hands-on experience with automation through my comprehensive **n8n tutorial playlist**, featuring 18+ complete, step-by-step videos and ready-to-use templates.
|
||||
|
||||
[](https://bit.ly/playlist-n8n-supplyscience)
|
||||
|
||||
|
||||
*This template was built using n8n v1.93.0*
|
||||
*Submitted: June 7, 2025*
|
||||
+1352
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,74 @@
|
||||
***Tags**: Sustainability, Business Travel, Carbon Emissions, Flight Tracking, Carbon Interface API*
|
||||
|
||||
### Context
|
||||
|
||||
Hi! I’m Samir — a Supply Chain Engineer and Data Scientist based in Paris, and founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
I help companies monitor and reduce their environmental footprint by combining **AI automation**, **carbon estimation APIs**, and **workflow automation**.
|
||||
|
||||
This workflow is part of our **sustainability reporting initiative**, allowing businesses to **track the CO₂ emissions of employee flights**.
|
||||
|
||||
> Automate carbon tracking for your business travel with AI-powered workflows in n8n!
|
||||
|
||||
📬 For business inquiries, feel free to connect with me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow is designed for **travel managers**, **sustainability teams**, or **finance teams** who need to measure and report on emissions from business travel.
|
||||
|
||||
Let’s imagine your company receives a **flight confirmation email**:
|
||||
|
||||

|
||||
|
||||
The **AI Agent** reads the email and extracts structured data, such as **flight dates, airport codes, and number of passengers**.
|
||||
|
||||
Then, the **Carbon Interface API** is called to estimate **CO₂ emissions**, which are stored in a **Google Sheet** for sustainability reporting.
|
||||
|
||||

|
||||
|
||||
### How does it work?
|
||||
|
||||
This workflow automates the end-to-end process of **tracking flight emissions** from email to CO₂ estimation:
|
||||
|
||||
- 📨 Gmail Trigger captures booking confirmations
|
||||
- 🧠 AI Agent extracts structured data (airports, dates, flight numbers)
|
||||
- ✈️ Each flight leg is processed individually
|
||||
- 🌍 Carbon Interface API returns distance and carbon emissions
|
||||
- 📄 A second Google Sheet node appends the emission data for reporting
|
||||
|
||||
### Steps:
|
||||
|
||||
1. 💌 Trigger on new flight confirmation email
|
||||
2. 🧠 Extract structured trip data using AI Agent (flights, airports, dates)
|
||||
3. 📑 Store flight metadata in Google Sheets
|
||||
4. 🧭 For each leg, call the Carbon Interface API
|
||||
5. 📥 Append distance, CO₂ in kg, and timestamp to the flight row
|
||||
|
||||

|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
You’ll need:
|
||||
|
||||
- A **Gmail account** receiving SAP Concur or travel confirmation emails
|
||||
- A **Google Sheet** to record trip metadata and CO₂ emissions
|
||||
- A free [Carbon Interface API key](https://www.carboninterface.com/docs)
|
||||
- Access to OpenAI for parsing the email via AI Agent
|
||||
- A few sample flight confirmation emails to test
|
||||
|
||||
### 📺 Learn More with n8n Tutorials
|
||||
|
||||
Get hands-on experience with automation through my comprehensive **n8n tutorial playlist**, featuring 18+ complete, step-by-step videos and ready-to-use templates.
|
||||
|
||||
[](https://bit.ly/playlist-n8n-supplyscience)
|
||||
|
||||
### Next Steps
|
||||
|
||||
🗒️ Use the sticky notes in the n8n canvas to:
|
||||
|
||||
- Add your Gmail and Carbon Interface credentials
|
||||
- Send a sample booking email to your inbox
|
||||
- Verify that emissions and distances are correctly added to your sheet
|
||||
|
||||
*This template was built using n8n v1.93.0*
|
||||
*Submitted: June 7, 2025*
|
||||
+677
@@ -0,0 +1,677 @@
|
||||
{
|
||||
"id": "1H8R2Hvre2Z1LuTG",
|
||||
"meta": {
|
||||
"instanceId": "",
|
||||
"templateCredsSetupCompleted": true
|
||||
},
|
||||
"name": "✈️ CO2 Emissions of Business Travels with CarbonInterface API and GPT-4o",
|
||||
"tags": [],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "16cb6418-383a-409e-ac18-8ba78aba031b",
|
||||
"name": "Gmail Trigger",
|
||||
"type": "n8n-nodes-base.gmailTrigger",
|
||||
"position": [
|
||||
-320,
|
||||
415
|
||||
],
|
||||
"parameters": {
|
||||
"simple": false,
|
||||
"filters": {},
|
||||
"options": {
|
||||
"downloadAttachments": false
|
||||
},
|
||||
"pollTimes": {
|
||||
"item": [
|
||||
{
|
||||
"mode": "everyMinute"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"gmailOAuth2": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "5c7c052e-9a71-4867-9d05-492dac9a8b7c",
|
||||
"name": "Structured Output Parser",
|
||||
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
|
||||
"position": [
|
||||
140,
|
||||
600
|
||||
],
|
||||
"parameters": {
|
||||
"jsonSchemaExample": "{\n \"traveler_name\": \"Samir Saci\",\n \"trip_purpose\": \"Sustainability Client Workshop\",\n \"trip_id\": \"LG-TRIP-2025-0624\",\n \"flights\": [\n {\n \"type\": \"departure\",\n \"from\": \"Paris Charles de Gaulle (CDG)\",\n \"to\": \"Toronto Pearson International (YYZ)\",\n \"from_code\": \"CDG\",\n \"to_code\": \"YYZ\",\n \"date\": \"2025-06-24\",\n \"flight_number\": \"AF356\"\n },\n {\n \"type\": \"return\",\n \"from\": \"Toronto Pearson International (YYZ)\",\n \"to\": \"Paris Charles de Gaulle (CDG)\",\n \"from_code\": \"YYZ\",\n \"to_code\": \"CDG\",\n \"date\": \"2025-06-28\",\n \"flight_number\": \"AC880\"\n }\n ],\n \"hotel_booked\": false,\n \"ground_transport_booked\": false\n}\n"
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "c0eeae42-d3dd-400e-8d19-1068f4ab999d",
|
||||
"name": "AI Agent Parser",
|
||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||
"position": [
|
||||
-100,
|
||||
415
|
||||
],
|
||||
"parameters": {
|
||||
"text": "=Extract the structured trip details from the following email:\n\n{{ $json.text }}\n",
|
||||
"options": {
|
||||
"systemMessage": "=You are a travel assistant. When provided with the body of a business trip confirmation email, extract the following structured JSON object.\n\nReturn **only** a JSON object with the following fields:\n\n{\n \"traveler_name\": string,\n \"trip_purpose\": string,\n \"trip_id\": string,\n \"flights\": [\n {\n \"type\": \"departure\" or \"return\",\n \"from\": string,\n \"from_code\": string (e.g., \"CDG\"),\n \"to\": string,\n \"to_code\": string (e.g., \"YYZ\"),\n \"date\": string (ISO 8601, e.g., \"2025-06-24\"),\n \"flight_number\": string\n }\n ],\n \"hotel_booked\": boolean,\n \"ground_transport_booked\": boolean\n}\n\n- Extract the 3-letter airport codes (IATA format) from the airport names and populate `from_code` and `to_code`.\n- If hotel or ground transport is marked “Not booked,” return `false`, otherwise `true`.\n- Always include both departure and return flights as separate items in the \"flights\" array.\n- Return only the JSON object, no extra text.\n"
|
||||
},
|
||||
"promptType": "define",
|
||||
"hasOutputParser": true
|
||||
},
|
||||
"typeVersion": 1.9
|
||||
},
|
||||
{
|
||||
"id": "2a30a34e-76d2-4d89-a5f9-cf48f97f490e",
|
||||
"name": "OpenAI Chat Model2",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"position": [
|
||||
-100,
|
||||
620
|
||||
],
|
||||
"parameters": {
|
||||
"model": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gpt-4o-mini"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"credentials": {
|
||||
"openAiApi": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "f0eb3490-4f5f-42ca-83fa-0a562a08d630",
|
||||
"name": "Split Out",
|
||||
"type": "n8n-nodes-base.splitOut",
|
||||
"position": [
|
||||
320,
|
||||
420
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"fieldToSplitOut": "output.flights"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "c7bb9d8b-a972-4d53-8217-8e77c29dda81",
|
||||
"name": "Record Flights Information",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
840,
|
||||
360
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"To": "={{ $json.to }}",
|
||||
"From": "={{ $json.from }}",
|
||||
"To Code": "={{ $json.to_code }}",
|
||||
"Trip ID": "={{ $('AI Agent Parser').item.json.output.trip_id }}_{{ $json.type }}",
|
||||
"From Code": "={{ $json.from_code }}",
|
||||
"Flight Date": "={{ $json.date }}",
|
||||
"Flight Type": "={{ $json.type }}",
|
||||
"Hotel Booked": "={{ $('AI Agent Parser').item.json.output.hotel_booked }}",
|
||||
"Trip Purpose": "={{ $('AI Agent Parser').item.json.output.trip_purpose }}",
|
||||
"Flight Number": "={{ $json.flight_number }}",
|
||||
"Traveler Name": "={{ $('AI Agent Parser').item.json.output.traveler_name }}",
|
||||
"Ground Transport Booked": "={{ $('AI Agent Parser').item.json.output.ground_transport_booked }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "Traveler Name",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Traveler Name",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Trip Purpose",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Trip Purpose",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Trip ID",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Trip ID",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Hotel Booked",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Hotel Booked",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Ground Transport Booked",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Ground Transport Booked",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Flight Type",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Flight Type",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "From",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "From",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "From Code",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "From Code",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "To",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "To",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "To Code",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "To Code",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Flight Date",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Flight Date",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Flight Number",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Flight Number",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Flight"
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "1Unyl-tEDYwtrILjNLOX7IbBP-TAYy8gcmqcZahPpiEI",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Carbon Emissions"
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.6
|
||||
},
|
||||
{
|
||||
"id": "9a3da134-6af3-4130-9420-7efbd4d47d17",
|
||||
"name": "Loop Over Flights",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"position": [
|
||||
540,
|
||||
420
|
||||
],
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "8fa6af96-da24-449f-ab07-7c26eebf82e1",
|
||||
"name": "Collect CO2 Emissions",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
1040,
|
||||
360
|
||||
],
|
||||
"parameters": {
|
||||
"url": "https://www.carboninterface.com/api/v1/estimates",
|
||||
"method": "POST",
|
||||
"options": {},
|
||||
"jsonBody": "={\n \"type\": \"flight\",\n \"passengers\": 1,\n \"legs\": [\n {\n \"departure_airport\": \"{{ $json['From Code'] }}\",\n \"destination_airport\": \"{{ $json['To Code'] }}\"\n }\n ]\n}\n",
|
||||
"sendBody": true,
|
||||
"sendHeaders": true,
|
||||
"specifyBody": "json",
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_TOKEN_HERE"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "0a297fa4-3ea0-485d-901e-48f8e3d32840",
|
||||
"name": "Load Results",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
1300,
|
||||
420
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"Trip ID": "={{ $('Record Flights Information').item.json['Trip ID'] }}",
|
||||
"CO2 (kg)": "={{ $json.data.attributes.carbon_kg }}",
|
||||
"Distance (km)": "={{ $json.data.attributes.distance_value }}",
|
||||
"Estimation Time": "={{ $json.data.attributes.estimated_at }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "Traveler Name",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "Traveler Name",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Trip Purpose",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "Trip Purpose",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Trip ID",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "Trip ID",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Hotel Booked",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "Hotel Booked",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Ground Transport Booked",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "Ground Transport Booked",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Flight Type",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "Flight Type",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "From",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "From",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "From Code",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "From Code",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "To",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "To",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "To Code",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "To Code",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Flight Date",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "Flight Date",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Flight Number",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "Flight Number",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Distance (km)",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Distance (km)",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "CO2 (kg)",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "CO2 (kg)",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Estimation Time",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Estimation Time",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [
|
||||
"Trip ID"
|
||||
],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "appendOrUpdate",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Flight"
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "1Unyl-tEDYwtrILjNLOX7IbBP-TAYy8gcmqcZahPpiEI",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Carbon Emissions"
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.6
|
||||
},
|
||||
{
|
||||
"id": "943097a7-84b2-4be1-a65a-711a8350daad",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-360,
|
||||
-60
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 180,
|
||||
"height": 820,
|
||||
"content": "### 1. Workflow Trigger with Gmail Trigger\nThe workflow is triggered by a new email received in your Gmail mailbox dedicated to process **flight schedules**.\n\n#### How to setup?\n- **Gmail Trigger Node:** set up your Gmail API credentials\n[Learn more about the Gmail Trigger Node](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.gmailtrigger)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "38d4649e-87c0-4bd6-b822-b88ff68309cc",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-160,
|
||||
-60
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 440,
|
||||
"height": 820,
|
||||
"content": "### 2. AI Agent to trip schedule report\nThis node will analyze the content of the email to extract information about the trip including **departure** and **return** trips with **airport codes** and booking information. \n\n#### How to setup?\n- **AI Agent with the Chat Model**:\n 1. Add a **chat model** with the required credentials *(Example: Open AI 4o-mini)*\n 2. Adapt the system prompt to the format of emails you expect to receive *(type of information included)*\n [Learn more about the AI Agent Node](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "01acc971-5482-4e00-846b-584660ec30ab",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
300,
|
||||
-60
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1180,
|
||||
"height": 820,
|
||||
"content": "### 3. Record Shipment Request Information and fetch distance and driving time using Open Route API\nThis starts by recording all the information parsed by the AI node. Then, we use the **Open Route API** to complete with **geocoding data** *(GPS Coordinates)* that will be used to fetch **driving distance and time**.\n\n#### How to setup?\n- **Setup Carbone Interface API Credentials**\n 1. Get your free API key: [Carbon Interface API Documentation](https://docs.carboninterface.com/#/)\n 2. Fill the API key in the HTTP request node\n- **Load records in the Google Sheet Node**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which you want to record your working sessions\n 4. Map the fields: **traveler_name**, **trip_purpose**, **trip_id**, **hotel_booked**, **ground_transport_booked**, **flight_type**, **from**, **from_code**, **to**, **to_code**, **flight_date**, **flight_number**, **distance_km**, **carbon_kg**, **estimated_at**\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n\nDistance, carbon emissions and estimation time will be fetched from the **Carbone Interface API**.\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"active": false,
|
||||
"pinData": {},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "8a89f371-efb3-4bd0-8909-8fb802f7826d",
|
||||
"connections": {
|
||||
"Split Out": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Flights",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Load Results": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Flights",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Gmail Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent Parser",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"AI Agent Parser": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Split Out",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Loop Over Flights": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Record Flights Information",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"OpenAI Chat Model2": {
|
||||
"ai_languageModel": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent Parser",
|
||||
"type": "ai_languageModel",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Collect CO2 Emissions": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Load Results",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Structured Output Parser": {
|
||||
"ai_outputParser": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent Parser",
|
||||
"type": "ai_outputParser",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Record Flights Information": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Collect CO2 Emissions",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
*Tags: Automation, AI, Marketing, Content Creation*
|
||||
|
||||
### Context
|
||||
I’m a **Supply Chain Data Scientist** and content creator who writes regularly about **data-driven optimization, logistics, and sustainability**. Promoting blog articles on LinkedIn used to be a manual task — until I decided to **automate it with N8N and GPT-4o**.
|
||||
|
||||
This workflow lets you **automatically extract blog posts**, clean the content, and generate a **professional LinkedIn post** using an **AI Agent powered by GPT-4o** — all in one seamless automation.
|
||||
|
||||
>Save hours of repetitive work and boost your reach with AI.
|
||||
|
||||
📬 For business inquiries, you can add me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
This template is perfect for:
|
||||
- **Bloggers and writers** who want to promote their content on LinkedIn
|
||||
- **Marketing teams** looking to automate professional post-generation
|
||||
- **Content creators** using Ghost platforms
|
||||
|
||||
It generates polished LinkedIn posts with:
|
||||
- A hook
|
||||
- A quick summary
|
||||
- A call-to-action
|
||||
- A signature to drive readers to your contact page
|
||||
|
||||

|
||||
|
||||
### How does it work?
|
||||
This workflow runs in N8N and performs the following steps:
|
||||
|
||||
1. 🚀 **Triggers manually** or you can add a scheduler
|
||||
2. 📰 **Pulls recent blog posts** from your Ghost site (via API)
|
||||
3. 🧼 **Cleans the HTML** content for AI input
|
||||
4. 🤖 **Sends content to GPT-4o** with a tailored prompt to create a LinkedIn post
|
||||
5. 📄 **Records all data** (post content + LinkedIn output) in a Google Sheet
|
||||
|
||||

|
||||
|
||||
### What do I need to start?
|
||||
You don’t need to write a single line of code.
|
||||
#### Prerequisites:
|
||||
- A **Ghost CMS account** with blog content
|
||||
- A **Google Sheet** to store generated posts
|
||||
- An **OpenAI API Key**
|
||||
- **Google Sheets API** connected via OAuth2
|
||||
|
||||
### Next Steps
|
||||
Use the sticky notes in the workflow to understand how to:
|
||||
- Add your Ghost API credentials
|
||||
- Link your Google Sheet
|
||||
- Customize the AI prompt (e.g., change the author name or tone)
|
||||
- Optionally add auto-posting to LinkedIn using tools like Buffer or Make
|
||||
|
||||
[](https://www.youtube.com/watch?v=Lhi6hV6rWEo)
|
||||
|
||||
[🎥 Watch My Tutorial](https://www.youtube.com/watch?v=Lhi6hV6rWEo)
|
||||
|
||||
🚀 Want to explore how automation can scale your brand or business?
|
||||
📬 Let’s connect on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Notes
|
||||
- You can adapt this template for **Twitter, Facebook**, or even **email newsletters** by adjusting the prompt and output channel.
|
||||
- This workflow was built using **n8n 1.85.4**
|
||||
|
||||
Submitted: April 9th, 2025
|
||||
@@ -0,0 +1,477 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": "=",
|
||||
"templateCredsSetupCompleted": true
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "4815105b-4175-45ad-85bc-07917de9526c",
|
||||
"name": "When clicking ‘Test workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"position": [
|
||||
-140,
|
||||
-720
|
||||
],
|
||||
"parameters": {},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "b8f2a706-4868-4f0d-99a1-c31e1f7022e3",
|
||||
"name": "AI Agent",
|
||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||
"position": [
|
||||
1220,
|
||||
-580
|
||||
],
|
||||
"parameters": {
|
||||
"text": "=Article Title: {{ $json.title }}\nArticle Link: {{ $json.link }}\nArticle Content: {{ $json.clean_content }}",
|
||||
"options": {
|
||||
"systemMessage": "=You are a content marketing assistant. Based on the article metadata (ID, title) and cleaned content, generate a short LinkedIn promotional message for a professional audience.\n\nFollow this structure:\n\nStart with a hook that grabs attention (a bold insight, surprising fact, or thought-provoking question).\n\nBriefly summarize the article’s value — what readers will learn or gain from it.\n\nInclude a clear call-to-action encouraging readers to read the article.\n\nEnd with this author signature and invitation:\n“—\nSamir Saci\nSupply Chain Data Scientist & Founder of LogiGreen\n📩 Contact me: https://logi-green.com/contactus”\n\nUse a professional and engaging tone. Do not include hashtags or Markdown formatting."
|
||||
},
|
||||
"promptType": "define"
|
||||
},
|
||||
"typeVersion": 1.8
|
||||
},
|
||||
{
|
||||
"id": "ac1538f6-67ef-4fd0-b4a9-d44b49149e5f",
|
||||
"name": "OpenAI Chat Model",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"position": [
|
||||
1160,
|
||||
-420
|
||||
],
|
||||
"parameters": {
|
||||
"model": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gpt-4o-mini"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "bac79ecf-b92d-42ba-bb0f-f1e1f85ca1c9",
|
||||
"name": "Clean HTML",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
780,
|
||||
-620
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "const htmlContent = $input.first().json.content;\n\nconst cleanText = htmlContent\n .replace(/<[^>]*>/g, '') // remove tags\n .replace(/\\s+/g, ' ') // normalize spaces\n .replace(/ /g, ' ') // decode common entity\n .trim();\n\nreturn [\n {\n json: {\n clean_content: cleanText\n }\n }\n];\n"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "fa57b494-370a-4f37-bcbe-38ba6138da76",
|
||||
"name": "Extract Blog Posts",
|
||||
"type": "n8n-nodes-base.ghost",
|
||||
"position": [
|
||||
80,
|
||||
-720
|
||||
],
|
||||
"parameters": {
|
||||
"limit": 3,
|
||||
"options": {},
|
||||
"operation": "getAll"
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "dc19b6a4-fa17-41b4-8f8c-352519f07569",
|
||||
"name": "Extract Post Content",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"position": [
|
||||
300,
|
||||
-720
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "00b337cd-1c61-4f19-8c51-b76f3a8dece1",
|
||||
"name": "id",
|
||||
"type": "string",
|
||||
"value": "={{ $json.id }}"
|
||||
},
|
||||
{
|
||||
"id": "8d38f4bc-bca6-4343-8c5e-5d9fd9cbe178",
|
||||
"name": "title",
|
||||
"type": "string",
|
||||
"value": "={{ $json.title }}"
|
||||
},
|
||||
{
|
||||
"id": "c34ddd76-0db6-4225-82fa-04d5542f9c7c",
|
||||
"name": "featured_image",
|
||||
"type": "string",
|
||||
"value": "={{ $json.feature_image }}"
|
||||
},
|
||||
{
|
||||
"id": "c0f9593c-0d5a-4659-9e25-91b098318bd6",
|
||||
"name": "excerpt",
|
||||
"type": "string",
|
||||
"value": "={{ $json.excerpt }}"
|
||||
},
|
||||
{
|
||||
"id": "0d11d3d5-49f8-473a-8602-b49769f88005",
|
||||
"name": "content",
|
||||
"type": "string",
|
||||
"value": "={{ $json.html }}"
|
||||
},
|
||||
{
|
||||
"id": "ec89a00d-9d76-4594-a8ce-98aa177e6737",
|
||||
"name": "link",
|
||||
"type": "string",
|
||||
"value": "={{ $json.url }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 3.4
|
||||
},
|
||||
{
|
||||
"id": "45656e13-5f03-48f9-8422-0ea3993e3289",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-180,
|
||||
-1080
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 200,
|
||||
"height": 520,
|
||||
"content": "### 1. Workflow Trigger\nThis workflow uses simple trigger.\n\n#### How to setup?\n*Nothing to do.*\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "7b8c3c49-069f-464b-acd2-a1a047fb2138",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
40,
|
||||
-1080
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 400,
|
||||
"height": 520,
|
||||
"content": "### 2. Extract Blog Posts Content\nThe Ghost node extracts all the posts of your blog with content and metadata. In the second node, we extract description, URL, content and features image url.\n\n#### How to setup?\n- **Ghost Account API**:\n 1. Add your Ghost Blog Account Credentials\n 2. Select the number of Blog Posts you want to collect\n [Learn more about the Ghost Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.ghost)\n\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "0a5e4045-7df2-4713-a475-509844c58344",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
480,
|
||||
-1080
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1520,
|
||||
"height": 800,
|
||||
"content": "### 3. Generate a Linkedin Post for each Post with an AI Agent\nThis block loops through all the posts pulled by the Ghost Node, send the content to the AI agent that generates a Linkedin post. The results are combined and pulled in a Google Sheet.\n\n#### How to setup?\n- **AI Agent with the Chat Model**:\n 1. Add a **chat model** with the required credentials *(Example: Open AI 4o-mini)*\n 2. Adapt the system prompt with your **post signature** and additional points you want to add in your posts\n [Learn more about the AI Agent Node](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent)\n- **Record Long Break in the Google Sheet Node**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which you want to record your working sessions\n 4. Map the fields\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "29b09c7f-c39c-414d-b9d5-897b0d540328",
|
||||
"name": "Record the posts",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
1840,
|
||||
-480
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"id": "={{ $json.id }}",
|
||||
"title": "={{ $json.title }}",
|
||||
"content": "={{ $json.content }}",
|
||||
"excerpt": "={{ $json.excerpt }}",
|
||||
"clean_content": "={{ $json.clean_content }}",
|
||||
"linkedin_post": "={{ $json.output }}",
|
||||
"featured_image": "={{ $json.featured_image }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "id",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "id",
|
||||
"defaultMatch": true,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "title",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "title",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "featured_image",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "featured_image",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "excerpt",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "excerpt",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "content",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "content",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "clean_content",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "clean_content",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "linkedin_post",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "linkedin_post",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [
|
||||
"id"
|
||||
],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "=",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.5
|
||||
},
|
||||
{
|
||||
"id": "6f1c58db-a4bf-421a-a182-8149dac28725",
|
||||
"name": "Merge Linkedin",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"position": [
|
||||
1600,
|
||||
-720
|
||||
],
|
||||
"parameters": {
|
||||
"mode": "combineBySql"
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "ebae3ccc-2727-44d9-9309-320c7d8e8349",
|
||||
"name": "Add Clean HTML",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"position": [
|
||||
1020,
|
||||
-720
|
||||
],
|
||||
"parameters": {
|
||||
"mode": "combineBySql"
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "d839ca8d-f898-4617-955f-9c6d9a5412b7",
|
||||
"name": "Loop Over Posts",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"position": [
|
||||
580,
|
||||
-720
|
||||
],
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "7e759203-b524-4cc5-89df-5e113c800504",
|
||||
"name": "Sticky Note3",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-180,
|
||||
-540
|
||||
],
|
||||
"parameters": {
|
||||
"width": 660,
|
||||
"height": 460,
|
||||
"content": "### [📺Complete Tutorial](https://www.youtube.com/watch?v=Lhi6hV6rWEo)\n\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"AI Agent": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge Linkedin",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Clean HTML": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Add Clean HTML",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Add Clean HTML": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge Linkedin",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Merge Linkedin": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Record the posts",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Loop Over Posts": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Clean HTML",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Add Clean HTML",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Record the posts": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Posts",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"OpenAI Chat Model": {
|
||||
"ai_languageModel": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent",
|
||||
"type": "ai_languageModel",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Blog Posts": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Post Content",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Post Content": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Posts",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Blog Posts",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
***Tags**: Ghost CMS, SEO Audit, Image Optimisation, Alt Text, Google Sheets, Automation*
|
||||
|
||||
### Context
|
||||
|
||||
Hi! I’m Samir — a Supply Chain Engineer and Data Scientist based in Paris, and founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
I help companies and content creators use automation and analytics to **improve visibility**, **enhance performance**, and **reduce manual work**.
|
||||
|
||||
> Let’s use n8n to automate SEO audits to increase your traffic!
|
||||
|
||||
📬 For business inquiries, feel free to connect on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow is perfect for **bloggers, marketers, or content teams** using **Ghost CMS** who want to:
|
||||
|
||||
- Extract and review **all images** from articles
|
||||
- Detect missing or short **alt texts**
|
||||
- Check image **file size** and **filename SEO compliance**
|
||||
- Push the audit results into a **Google Sheet**
|
||||
|
||||

|
||||
|
||||
### How does it work?
|
||||
|
||||
This n8n workflow extracts all blog posts from Ghost CMS, scans the HTML to collect all embedded images, then evaluates each image for:
|
||||
|
||||
- ✅ Presence and length of `alt text`
|
||||
- 📏 File size in kilobytes
|
||||
- 🔤 Filename SEO quality (e.g. lowercase, hyphenated, no special chars)
|
||||
|
||||
All findings are written to Google Sheets for **further analysis** or **manual cleanup**.
|
||||
|
||||
### 🧭 Workflow Steps:
|
||||
|
||||
1. 🚀 Trigger the workflow manually or on schedule
|
||||
2. 📰 Extract blog post content from Ghost CMS
|
||||
3. 🖼️ Parse all `<img>` tags with `src` and `alt` attributes
|
||||
4. 📤 Store image metadata in a Google Sheet (step 1)
|
||||
5. 🌐 Download each image using HTTP request
|
||||
6. 🧮 Extract file size, extension, and filename SEO flag
|
||||
7. 📄 Update the audit sheet with size and format insights
|
||||
|
||||

|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
This workflow requires:
|
||||
|
||||
- A **Ghost Content API key**
|
||||
- A **Google Sheet** (to log audit results)
|
||||
- No AI or external APIs required — works fully with built-in nodes
|
||||
-
|
||||
### 📺 Learn More with n8n Tutorials
|
||||
|
||||
Get hands-on experience with automation through my comprehensive **n8n tutorial playlist**, featuring 18+ complete, step-by-step videos and ready-to-use templates.
|
||||
|
||||
[](https://bit.ly/playlist-n8n-supplyscience)
|
||||
|
||||
|
||||
### Next Steps
|
||||
|
||||
🗒️ Follow the sticky notes inside the workflow to:
|
||||
|
||||
- Plug in your Ghost blog credentials
|
||||
- Select or create a Google Sheet
|
||||
- Run the audit and start improving your SEO!
|
||||
|
||||
*This template was built using n8n v1.93.0*
|
||||
*Submitted: June 8, 2025*
|
||||
|
||||
@@ -0,0 +1,644 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": "",
|
||||
"templateCredsSetupCompleted": true
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "f20cd1d4-671c-420c-a25d-1c9fe05b67d9",
|
||||
"name": "When clicking ‘Test workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"position": [
|
||||
-1000,
|
||||
-1030
|
||||
],
|
||||
"parameters": {},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "edf80cc8-a4b3-42dd-96dc-c729d209aef9",
|
||||
"name": "Extract Blog Posts",
|
||||
"type": "n8n-nodes-base.ghost",
|
||||
"position": [
|
||||
-780,
|
||||
-1030
|
||||
],
|
||||
"parameters": {
|
||||
"limit": 1,
|
||||
"options": {},
|
||||
"operation": "getAll"
|
||||
},
|
||||
"credentials": {
|
||||
"ghostContentApi": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "b2f0a448-5b73-499a-a78e-e69b75fc8f9d",
|
||||
"name": "Extract Post Content",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"position": [
|
||||
-560,
|
||||
-1030
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "00b337cd-1c61-4f19-8c51-b76f3a8dece1",
|
||||
"name": "id",
|
||||
"type": "string",
|
||||
"value": "={{ $json.id }}"
|
||||
},
|
||||
{
|
||||
"id": "8d38f4bc-bca6-4343-8c5e-5d9fd9cbe178",
|
||||
"name": "title",
|
||||
"type": "string",
|
||||
"value": "={{ $json.title }}"
|
||||
},
|
||||
{
|
||||
"id": "c34ddd76-0db6-4225-82fa-04d5542f9c7c",
|
||||
"name": "featured_image",
|
||||
"type": "string",
|
||||
"value": "={{ $json.feature_image }}"
|
||||
},
|
||||
{
|
||||
"id": "c0f9593c-0d5a-4659-9e25-91b098318bd6",
|
||||
"name": "excerpt",
|
||||
"type": "string",
|
||||
"value": "={{ $json.excerpt }}"
|
||||
},
|
||||
{
|
||||
"id": "0d11d3d5-49f8-473a-8602-b49769f88005",
|
||||
"name": "content",
|
||||
"type": "string",
|
||||
"value": "={{ $json.html }}"
|
||||
},
|
||||
{
|
||||
"id": "ec89a00d-9d76-4594-a8ce-98aa177e6737",
|
||||
"name": "link",
|
||||
"type": "string",
|
||||
"value": "={{ $json.url }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 3.4
|
||||
},
|
||||
{
|
||||
"id": "e80bde5d-28c0-4fa7-a7ff-bff0fcddbf94",
|
||||
"name": "Loop Over Items",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"position": [
|
||||
-360,
|
||||
-800
|
||||
],
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "1052cc12-c1cd-468f-9cf7-33f73ff43642",
|
||||
"name": "Collect All Images",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
500,
|
||||
-1300
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "const results = [];\n\nfor (const item of items) {\n const articleUrl = item.json.link;\n const articleTitle = item.json.title;\n const articleId = item.json.id;\n const html = item.json.content;\n\n const imgRegex = /<img[^>]*src=\"([^\"]+)\"[^>]*alt=\"([^\"]*)\"?[^>]*>/g;\n let match;\n\n while ((match = imgRegex.exec(html)) !== null) {\n const imageUrl = match[1];\n const altText = match[2];\n\n results.push({\n json: {\n articleId: articleId,\n articleTitle: articleTitle,\n article_url: articleUrl,\n image_url: imageUrl,\n alt_text: altText\n }\n });\n }\n}\n\nreturn results;\n"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "b2176289-ded3-471c-bc8a-225dacc33aeb",
|
||||
"name": "Add Records",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
720,
|
||||
-1300
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"alt_text": "={{ $json.alt_text }}",
|
||||
"articleId": "={{ $json.articleId }}",
|
||||
"image_url": "={{ $json.image_url }}",
|
||||
"article_url": "={{ $json.article_url }}",
|
||||
"articleTitle": "={{ $json.articleTitle }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "articleId",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "articleId",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "articleTitle",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "articleTitle",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "article_url",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "article_url",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "image_url",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "image_url",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "alt_text",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "alt_text",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": 886110184,
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "1Nk_2xwvK0ipSjnSbP50AQFc92IlHWaOkSK57-vt_3GY",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.6
|
||||
},
|
||||
{
|
||||
"id": "cdb245eb-a3e3-45d1-bcce-f1d0ce65297c",
|
||||
"name": "Collect Records by Id",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
-140,
|
||||
-760
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"filtersUI": {
|
||||
"values": [
|
||||
{
|
||||
"lookupValue": "={{ $json.id }}",
|
||||
"lookupColumn": "articleId"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": 886110184,
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "1Nk_2xwvK0ipSjnSbP50AQFc92IlHWaOkSK57-vt_3GY",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "ePnaAS4PG1sN2E50",
|
||||
"name": "Google Sheets LogiGreenBot"
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.6
|
||||
},
|
||||
{
|
||||
"id": "dbec4c36-c7e3-49ea-976d-cec6dee70f76",
|
||||
"name": "Loop Over Items1",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"position": [
|
||||
80,
|
||||
-760
|
||||
],
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "0254622b-3593-4ad8-820c-f6e27e31c53c",
|
||||
"name": "Get Image",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
300,
|
||||
-780
|
||||
],
|
||||
"parameters": {
|
||||
"url": "={{ $json.image_url }}",
|
||||
"options": {
|
||||
"response": {
|
||||
"response": {
|
||||
"responseFormat": "file"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "bb2a182b-82ae-4263-b3ab-9a7e30a5b97c",
|
||||
"name": "Extract Image Info",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
520,
|
||||
-780
|
||||
],
|
||||
"parameters": {
|
||||
"mode": "runOnceForEachItem",
|
||||
"jsCode": "const base64 = $binary[\"data\"].data;\nconst fileName = $binary[\"data\"].fileName || 'unknown.png';\nconst extension = fileName.split('.').pop().toLowerCase();\nconst fileSizeKB = $binary[\"data\"].fileSize || 'unknown.png';\nconst size_ok = fileSizeKB <= 500 ? true : false;\n\nreturn {\n json: {\n file_name: fileName,\n file_size_kb: fileSizeKB,\n format: extension,\n filename_seo: /[a-z0-9-]{3,}/.test(fileName) ? true : false,\n size_ok: size_ok\n }\n};\n"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "871e3d19-87c7-4dd6-a481-d83fc652478b",
|
||||
"name": "Results Images",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
740,
|
||||
-680
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"format": "={{ $json.format }}",
|
||||
"size_ok": "={{ $json.size_ok }}",
|
||||
"image_url": "={{ $('Get Image').item.json.image_url }}",
|
||||
"file_size_kb": "={{ $json.file_size_kb }}",
|
||||
"filename_seo": "={{ $json.filename_seo }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "articleId",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "articleId",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "articleTitle",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "articleTitle",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "article_url",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "article_url",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "image_url",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "image_url",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "alt_text",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "alt_text",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "file_size_kb",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "file_size_kb",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "format",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "format",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "filename_seo",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "filename_seo",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "size_ok",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "size_ok",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [
|
||||
"image_url"
|
||||
],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "appendOrUpdate",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": 886110184,
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "1Nk_2xwvK0ipSjnSbP50AQFc92IlHWaOkSK57-vt_3GY",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.6
|
||||
},
|
||||
{
|
||||
"id": "ad236989-0707-40ca-885c-c5d5550d7954",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-820,
|
||||
-1380
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 400,
|
||||
"height": 520,
|
||||
"content": "### 2. Extract Blog Posts Content\nThe Ghost node extracts all the posts of your blog with content and metadata. In the second node, we extract description, URL, content and features image url.\n\n#### How to setup?\n- **Ghost Account API**:\n 1. Add your Ghost Blog Account Credentials\n 2. Select the number of Blog Posts you want to collect\n [Learn more about the Ghost Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.ghost)\n\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "8d296af6-9e32-4669-bacc-decab229847a",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-1040,
|
||||
-1380
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 200,
|
||||
"height": 520,
|
||||
"content": "### 1. Workflow Trigger\nThis workflow uses simple trigger.\n\n#### How to setup?\n*Nothing to do.*\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "092719b3-65d7-40b8-af99-36cd3f41ea55",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-400,
|
||||
-1380
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1360,
|
||||
"height": 280,
|
||||
"content": "### 3. First Loop to Collect Images\nWe loop through all the articles extracted by the Ghost Node. For each article, this section will collect all the images of the article with their **image_url** and **alt_text**. The results will be recorded in a **Google Sheet**.\n\n#### How to setup?\n- **Record Long Break in the Google Sheet Node**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which you want to record the images information\n 4. Map the fields: **article_id**, **article_Title**, **article_url**, **image_url**, **alt_text**, **file_size_kb**, **format**, **filename_seo**\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n\n\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "71a03b91-fa30-4ede-88f4-f307b5fd7889",
|
||||
"name": "Sticky Note3",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-400,
|
||||
-1080
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1360,
|
||||
"height": 620,
|
||||
"content": "### 4. Second Loop to Extract Image Information\nWe loop through all the images in the article, for each image we **download** the content using the **HTTP request** node to record **image_size** in kilobytes and **image_name** in a Google Sheet. If the image size is higher than **500 kb**, it is flagged as **not ok**.\n#### How to setup?\n- **Record Image File Name and Size**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which you want to record the images information\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n\n\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"Get Image": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Image Info",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Add Records": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Results Images": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Loop Over Items",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Loop Over Items": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Collect All Images",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Collect Records by Id",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Loop Over Items1": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Get Image",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Collect All Images": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Add Records",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Blog Posts": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Post Content",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Image Info": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Results Images",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Post Content": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Collect Records by Id": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Blog Posts",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
### Context
|
||||
Hey! I'm Samir, a **Supply Chain Data Scientist from Paris** who spent six years in China studying and working while **struggling to learn Mandarin.**
|
||||
|
||||

|
||||
|
||||
I know the challenges of **mastering a complex language** like Chinese and my greatest support was **flash cards**. Therefore, I designed this workflow to support fellow Mandarin learners by **automating flashcard creation** using n8n, so they can focus more on learning and less on manual data entry.
|
||||
|
||||
📬 For business inquiries, you can add me on [Here](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
This workflow template is designed for **language learners** and **educators** who want to automate the creation of flashcards for Mandarin (or any other language) using **Google Translate API**, an **AI agent** for phonetic transcription and generating an illustrative sentence and a **free image retrieval API**.
|
||||
|
||||

|
||||
|
||||
#### Why?
|
||||
If you use the open-source application **Anki**, this workflow will help you automatically generate personalized study materials.
|
||||
|
||||
#### How?
|
||||
Let us imagine you want to learn how to say the word **Contract** in Mandarin.
|
||||
|
||||
The workflow will automatically
|
||||
1. **Translate the word** in Simplified Mandarin *(Mandarin: 合同)*.
|
||||
2. Provide the **phonetic transcription** *(Pinyin: Hétóng)*
|
||||
3. Generate **an example sentence** *(Example: 我们签订了一份合同.)*
|
||||
4. Download an **illustrative picture** (For example, a picture of a contract signature)
|
||||
|
||||

|
||||
|
||||
All these fields are automatically recorded in a Google Sheet, making it easy to import into Anki and generate flashcards instantly
|
||||
|
||||
### What do I need to start?
|
||||
This workflow can be used with the free tier plans of the services used. It does not require any advanced programming skills.
|
||||
#### Prerequisite
|
||||
- A **Google Drive Account** with a folder including a Google Sheet
|
||||
- API Credentials: **Google Drive API**, **Google Sheets API** and **Google Translate API** activated with OAuth2 credentials
|
||||
- A free API key of pexels.com
|
||||
- A google sheet with the columns
|
||||
#### Next
|
||||
Follow the **sticky notes** to set up the parameters inside each node and get ready to pump your learning skills.
|
||||
|
||||
I have detailed the steps in a **short tutorial** 👇
|
||||
|
||||
[](https://youtu.be/2mRZJATUTDw)
|
||||
|
||||
🎥 [Check My Tutorial](https://youtu.be/2mRZJATUTDw)
|
||||
|
||||
### Notes
|
||||
- This workflow can be used **for any language**. In the AI Agent prompt, you just need to replace the word pinyin with phonetic transcription.
|
||||
- You can adapt the trigger to operate the workflow in the way you want. These operations can be performed by batch or triggered by Telegram, email, or webhook.
|
||||
- If you want to learn more about how I used Anki flash cards to learn mandarin: 🈷️ [Blog Article about Anki Flash Cards](https://www.samirsaci.com/automate-flash-cards-creation-for-language-learning-with-python/)
|
||||
|
||||
|
||||
*This workflow has been created with N8N 1.82.1*
|
||||
*Submitted: March 17th, 2025*
|
||||
+754
@@ -0,0 +1,754 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": "6a5e68bcca67c4cdb3e0b698d01739aea084e1ec06e551db64aeff43d174cb23",
|
||||
"templateCredsSetupCompleted": true
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "bc49829b-45f2-4910-9c37-907271982f14",
|
||||
"name": "Sticky Note3",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-2240,
|
||||
-840
|
||||
],
|
||||
"parameters": {
|
||||
"width": 780,
|
||||
"height": 540,
|
||||
"content": "### 5. Do you need more details?\nFind a step-by-step guide in this tutorial\n\n[🎥 Watch My Tutorial](https://youtu.be/2mRZJATUTDw)"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "f0defc20-f099-4c7c-83a7-bb687b86a6b7",
|
||||
"name": "Google Translate",
|
||||
"type": "n8n-nodes-base.googleTranslate",
|
||||
"notes": "Translation -> 中文",
|
||||
"position": [
|
||||
-3500,
|
||||
-420
|
||||
],
|
||||
"parameters": {
|
||||
"text": "={{ $json.initialText }}",
|
||||
"translateTo": "zh-CN"
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "588d011c-d7e0-4b31-87be-d0c7ff6bf4b7",
|
||||
"name": "AI Agent",
|
||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||
"notes": "Pinyin + Example",
|
||||
"position": [
|
||||
-3080,
|
||||
-380
|
||||
],
|
||||
"parameters": {
|
||||
"text": "={{ $json.translatedText }}",
|
||||
"options": {
|
||||
"systemMessage": "# Role\nYou are a helpful translation agent that will help users to extract the pinyin from Chinese characters to create flashcard for language learning.\n\n# Context\nYou will receive a word or sentence in simplified Chinese characters; you are expected to extract the pinyin and generate a simple sentence in Chinese to illustrate the sense of the word. \n\n# Tasks\n1. Generate the pinyin of the characters presented\n2. Propose a short sentence in mandarin to illustrate the definition of the word.\n\n# Notes\n- Generate the output in JSON format following the example below:\n{\"pinyin\": \"Cāngkù\",\n\"sentence\": \"卡车抵达仓库。\"}\n\n- Be very diligent in thinking about the task being asked from you.\n- Generate concise sentences as they need to fit in flash cards.\n"
|
||||
},
|
||||
"promptType": "define",
|
||||
"hasOutputParser": true
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.7
|
||||
},
|
||||
{
|
||||
"id": "cc04e0be-0eea-4d92-a85f-10bd75c03081",
|
||||
"name": "OpenAI Chat Model",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"position": [
|
||||
-3080,
|
||||
-200
|
||||
],
|
||||
"parameters": {
|
||||
"model": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gpt-4o-mini"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "d98262f4-9066-420b-a440-fdbc83ca0ef0",
|
||||
"name": "Structured Output Parser",
|
||||
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
|
||||
"position": [
|
||||
-2900,
|
||||
-200
|
||||
],
|
||||
"parameters": {
|
||||
"jsonSchemaExample": "{\n \"pinyin\": \"Cāngkù\",\n \"sentence\": \"货物存放在仓库里。\"\n}"
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "e4ed388f-7520-4df2-8e37-d2b85b1ce532",
|
||||
"name": "Merge",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"position": [
|
||||
-2580,
|
||||
-400
|
||||
],
|
||||
"parameters": {
|
||||
"mode": "combineBySql"
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "c1431b36-77ee-4b50-a4e1-05489e998894",
|
||||
"name": "Trigger Added Row",
|
||||
"type": "n8n-nodes-base.googleSheetsTrigger",
|
||||
"notes": "Write a word in a new row",
|
||||
"position": [
|
||||
-3920,
|
||||
-420
|
||||
],
|
||||
"parameters": {
|
||||
"event": "rowAdded",
|
||||
"options": {
|
||||
"valueRender": "UNFORMATTED_VALUE",
|
||||
"dateTimeRenderOption": "SERIAL_NUMBER"
|
||||
},
|
||||
"pollTimes": {
|
||||
"item": [
|
||||
{
|
||||
"mode": "everyMinute"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": 1051887098,
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "<YOUR_GOOGLE_SHEET_ID>",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "4bafdddc-3c7f-40d4-ab17-75c6728bd5a3",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"notes": "If cell empty, do nothing",
|
||||
"position": [
|
||||
-3500,
|
||||
-260
|
||||
],
|
||||
"parameters": {},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "adabd9e0-13de-409b-a3ce-3c76c9624fc0",
|
||||
"name": "Upload Picture",
|
||||
"type": "n8n-nodes-base.googleDrive",
|
||||
"position": [
|
||||
-2740,
|
||||
460
|
||||
],
|
||||
"parameters": {
|
||||
"name": "={{ $('Trigger Added Row').item.json.initialText }}.jpeg",
|
||||
"driveId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "My Drive",
|
||||
"cachedResultUrl": "https://drive.google.com/drive/my-drive",
|
||||
"cachedResultName": "My Drive"
|
||||
},
|
||||
"options": {},
|
||||
"folderId": {
|
||||
"__rl": true,
|
||||
"mode": "url",
|
||||
"value": ""
|
||||
}
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "9cfa6722-ff5e-4ee2-a4ca-2b4175767c84",
|
||||
"name": "Get Picture",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
-2900,
|
||||
460
|
||||
],
|
||||
"parameters": {
|
||||
"url": "={{ $json.photos[0].src.medium }}",
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "4bfa5fc7-dd76-4611-916e-5b33a0a9acdb",
|
||||
"name": "Call API Pexels",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
-3080,
|
||||
460
|
||||
],
|
||||
"parameters": {
|
||||
"url": "https://api.pexels.com/v1/search",
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"sendHeaders": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "query",
|
||||
"value": "={{ $('Trigger Added Row').item.json.initialText }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "<PEXELS_API_KEY>"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "01c5dc70-3cf5-483e-bad7-4814ca7b1f97",
|
||||
"name": "Take initialText",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"position": [
|
||||
-3240,
|
||||
460
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "80661db0-175f-4346-a95b-5d1e73f82fb8",
|
||||
"name": "entry",
|
||||
"type": "string",
|
||||
"value": "={{ $json.initialText }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 3.4
|
||||
},
|
||||
{
|
||||
"id": "7630fc44-3a0c-442b-9c3b-17bd831cdb50",
|
||||
"name": "Extract Image Link",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"position": [
|
||||
-2580,
|
||||
460
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "019a529f-9447-4d49-9a91-04666d2c8fb6",
|
||||
"name": "image_link",
|
||||
"type": "string",
|
||||
"value": "={{ $json.webContentLink }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 3.4
|
||||
},
|
||||
{
|
||||
"id": "69645673-87c7-48cc-982f-b4e747fdf1ec",
|
||||
"name": "Final Merge",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"position": [
|
||||
-2100,
|
||||
20
|
||||
],
|
||||
"parameters": {
|
||||
"mode": "combine",
|
||||
"options": {},
|
||||
"combineBy": "combineByPosition"
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "1ee7aca7-5b9d-424f-b49f-2ee9ca7fafdc",
|
||||
"name": "Extract Pinyin and Example",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"position": [
|
||||
-2780,
|
||||
-380
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "c67839b8-abd5-47c9-b1e2-db599fbb5e9e",
|
||||
"name": "phonetic",
|
||||
"type": "string",
|
||||
"value": "={{ $json.output.pinyin }}"
|
||||
},
|
||||
{
|
||||
"id": "3983d009-85c4-46fd-8651-90462249f164",
|
||||
"name": "example",
|
||||
"type": "string",
|
||||
"value": "={{ $json.output.sentence }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 3.4
|
||||
},
|
||||
{
|
||||
"id": "baee6926-5031-43fa-94b8-8c7d36a9a6f0",
|
||||
"name": "Extract Fields",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"notes": "Initial text and its translation",
|
||||
"position": [
|
||||
-3000,
|
||||
-540
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "6d08361f-bb45-40a0-9934-f1e7bf90a171",
|
||||
"name": "initialText",
|
||||
"type": "string",
|
||||
"value": "={{ $('Trigger Added Row').item.json.initialText }}"
|
||||
},
|
||||
{
|
||||
"id": "0a64ccc0-5a8b-4925-9111-6b7e8c0f9368",
|
||||
"name": "translatedText",
|
||||
"type": "string",
|
||||
"value": "={{ $json.translatedText }}"
|
||||
},
|
||||
{
|
||||
"id": "84a2cfbd-a6da-4a94-a26c-153cbf73fefb",
|
||||
"name": "image_name",
|
||||
"type": "string",
|
||||
"value": "={{ $('Trigger Added Row').item.json.initialText }}.jpeg"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 3.4
|
||||
},
|
||||
{
|
||||
"id": "1f86fb6b-3ec7-4ea3-9748-a69db5d0c9f2",
|
||||
"name": "initialText is empty?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"notes": "Verify is the word is not empty",
|
||||
"position": [
|
||||
-3700,
|
||||
-420
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 2,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "266614ab-f9e3-486d-929f-ce14ce67e5ff",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "notEmpty",
|
||||
"singleValue": true
|
||||
},
|
||||
"leftValue": "={{ $json.initialText }}",
|
||||
"rightValue": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 2.2
|
||||
},
|
||||
{
|
||||
"id": "5c1afb76-3b5a-4c35-80b9-4a05f2d2aa2d",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-3280,
|
||||
140
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 820,
|
||||
"height": 480,
|
||||
"content": "## 3. Retrieve Images from Pexels Free Database\nExtract from Google sheet the word you want to translate to download an illustrating image from the free database of pexels.com\n\n### How to set up?\n- **HTTP Request Node (Call API Pexels)**: add in the header field 'Authorization' the API key provided by Pexels. *(Register here for the free API key: https://www.pexels.com/onboarding/)*\n[Learn more about the HTTP Request Node](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/)\n- **Upload the picture to Google Drive**:\n 1. Add your Google Drive API credentials to access the folder for images\n 2. Select your parent drive using the list, an URL or ID\n 3. Select the folder in which you want to save the pictures using the list, an URL or ID\n [Learn more about the Google Drive Upload Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googledrive)\n\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "ebacc192-9dba-4cab-ae0f-d5a2e885c208",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-3960,
|
||||
-780
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 600,
|
||||
"height": 680,
|
||||
"content": "## 1. Google Sheet Trigger & Translation using API\nTrigger the workflow when the user adds a word in English in a new row of the column initialText.\n\n### How to set up?\n- **Trigger on Row Added of Google Sheet**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which the vocabulary list is stored\n [Learn more about the Google Sheet on Row Added Trigger](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.googlesheetstrigger/)\n- **Google Translate API**:\n 1. Add your Google Translate API credentials\n 2. Select the target language *(Exemple: ZH-CN for Mainland China Mandarin)*\n [Learn more about the Google Translate API Node](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.googlesheetstrigger/)\n\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "5bdeb1de-d535-484d-83d6-12d72d8e5ba7",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-3220,
|
||||
-800
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 760,
|
||||
"height": 740,
|
||||
"content": "## 2. Simple AI agent to get the phonetic transcription and generate an sentence example\nThe agent will take the translated word as an input and will output the phonetic transcription and the sentence.\n\n### How to set up?\n- **AI Agent with the Chat Model**:\n 1. Add a chat model with the required credentials *(Example: Open AI 4o-mini)*\n 2. Adapt the system prompt with the target translation language and the format of the sentence\n [Learn more about the AI Agent Node](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent)\n\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "4f3f2a71-f137-4e91-8b1e-8a7342bac293",
|
||||
"name": "Add Results in Sheet",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"notes": "initialtext, translation, sentence",
|
||||
"position": [
|
||||
-1940,
|
||||
20
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"phonetic": "={{ $json.phonetic }}",
|
||||
"sentence": "={{ $json.example }}",
|
||||
"image_link": "={{ $json.image_name }}",
|
||||
"image_name": "={{ $json.image_link }}",
|
||||
"initialText": "={{ $json.initialText }}",
|
||||
"translatedText": "={{ $json.translatedText }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "initialText",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "initialText",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "translatedText",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "translatedText",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "phonetic",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "phonetic",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "sentence",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "sentence",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "image_name",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "image_name",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "image_link",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "image_link",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "row_number",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"readOnly": true,
|
||||
"required": false,
|
||||
"displayName": "row_number",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [
|
||||
"initialText"
|
||||
],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "update",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "<YOUR_GOOGLE_SHEET_TAB_ID>",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "<YOUR_GOOGLE_SHEET_ID>",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.5
|
||||
},
|
||||
{
|
||||
"id": "42a20d7e-03bf-4f4e-877b-04ff185cbf1c",
|
||||
"name": "Sticky Note4",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-2220,
|
||||
-280
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 580,
|
||||
"height": 460,
|
||||
"content": "## 4. Combine Results to Update the Google Sheet\nCombine initial text, translation, example sentence and image link to fill the new row.\n\n### How to set up?\n- **Add Results in Google Sheet**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which the vocabulary list is stored\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"Merge": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Final Merge",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"AI Agent": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Pinyin and Example",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Final Merge": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Add Results in Sheet",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Picture": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Upload Picture",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Fields": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Upload Picture": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Image Link",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Call API Pexels": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get Picture",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Google Translate": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "AI Agent",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Take initialText": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Call API Pexels",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"OpenAI Chat Model": {
|
||||
"ai_languageModel": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent",
|
||||
"type": "ai_languageModel",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Trigger Added Row": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "initialText is empty?",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Take initialText",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Image Link": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Final Merge",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"initialText is empty?": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Google Translate",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Structured Output Parser": {
|
||||
"ai_outputParser": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent",
|
||||
"type": "ai_outputParser",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Pinyin and Example": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
_Tags: Sustainability, CSRD, Reporting, ESG, Compliance, Automation_
|
||||
|
||||
### Context
|
||||
|
||||
Hey! I'm Samir, a **Supply Chain Engineer and Data Scientist from Paris**, founder of
|
||||
[LogiGreen Consulting](https://www.logi-green.com/)
|
||||
|
||||
We help **companies automate sustainability workflows** using AI, Data Analytics, and No-Code tools like N8N.
|
||||
|
||||
> Sustainability Reporting meets Automation with n8n!
|
||||
|
||||
📬 For business inquiries, you can add me on [Here](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### What is a CSRD XHTML Report?
|
||||
|
||||
Under the **Corporate Sustainability Reporting Directive (CSRD)**, companies must publish their ESG disclosures in a **machine-readable XHTML format**, embedding **XBRL tags** that make the report structured and standardized.
|
||||
|
||||

|
||||
|
||||
These files must follow strict formatting and tagging rules to ensure **compliance**, **traceability**, and **accessibility** for both regulators and analysts.
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow is designed for **sustainability teams, ESG consultants**, or **developers** who want to **automatically check the structure and format** of CSRD reports submitted in XHTML.
|
||||
|
||||
### How does it work?
|
||||
|
||||
This N8N workflow automates the audit process:
|
||||
|
||||
1. **📤 Input Node** → Uploads or fetches the XHTML file via URL or Webhook.
|
||||
2. **🧪 Validates Structure** → Uses a custom code node to parse HTML and identify required tags (e.g., `<ix:nonNumeric>`, namespaces).
|
||||
3. **📋 Outputs a Report** → Returns a summary report of errors, warnings, and key metadata (like entity name, reporting period).
|
||||
4. **📤 Export Option** → Save the results in Google Sheets or send via email.
|
||||
|
||||
### Prerequisite
|
||||
|
||||
- A sample XHTML file that you can find in my [GitHub Repository](https://github.com/samirsaci/n8n-templates)
|
||||
- **Google Sheets API** and **OpenAI API** credentials
|
||||
|
||||
### Next Steps
|
||||
|
||||
Follow the **sticky notes** inside each node to adjust parsing rules or extend validation to specific XBRL tags relevant to your sector (e.g., GHG emissions, water usage).
|
||||
|
||||
**📺 Check my complete tutorial to understand how to use it: **
|
||||
|
||||
[](https://youtu.be/npeJZv5U7og)
|
||||
|
||||
🎥 [Check My Tutorial](https://youtu.be/npeJZv5U7og)
|
||||
|
||||
🚀 Interested in combining **CSRD compliance** with **automation and analytics**? Let’s connect on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Notes
|
||||
|
||||
- This workflow includes an example XHTML file to test the validator.
|
||||
- You can plug this into your internal systems or even extend it with AI to **auto-summarize the sustainability report**.
|
||||
|
||||
_This workflow has been created with N8N 1.82.1_
|
||||
_Submitted: April 3rd, 2025_
|
||||
@@ -0,0 +1,336 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": "="
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "a2d54127-d1d1-44d2-859e-b89e2e6c3b4d",
|
||||
"name": "If",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"position": [
|
||||
260,
|
||||
260
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 2,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "=",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "contains"
|
||||
},
|
||||
"leftValue": "={{ $json.subject }}",
|
||||
"rightValue": "CSRD Reporting"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 2.2
|
||||
},
|
||||
{
|
||||
"id": "6a664023-ea8c-4973-b3ac-13a9e0664a58",
|
||||
"name": "Check the format",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
960,
|
||||
260
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "const content = $input.first().json.xhtml_content;\n\n// Helper to extract tags\nfunction extractTags(tagName) {\n const regex = new RegExp(`<${tagName}[^>]*>(.*?)<\\\\/${tagName}>`, 'gs');\n let matches = [];\n let match;\n while ((match = regex.exec(content)) !== null) {\n matches.push(match[1].trim());\n }\n return matches;\n}\n\n// Basic Tests\nconst headerPresent = /<ix:header>/i.test(content);\nconst governanceTag = /<ix:nonNumeric[^>]*name=\"esrs:SustainabilityGovernance\"/i.test(content);\nconst strategyTag = /<ix:nonNumeric[^>]*name=\"esrs:StrategySustainability\"/i.test(content);\n\n// KPI Tags\nconst kpiTags = [\"esrs:GHGScope1Emissions\", \"esrs:GHGScope2Emissions\", \"esrs:GHGScope3Emissions\"];\nconst kpiMatches = kpiTags.filter(tag => content.includes(tag));\n\n// Check for empty tags\nconst emptyNonNumeric = (content.match(/<ix:nonNumeric[^>]*>\\s*<\\/ix:nonNumeric>/g) || []).length;\n\n// Check duplicate text\nconst nonNumericValues = extractTags(\"ix:nonNumeric\");\nconst duplicates = [...new Set(nonNumericValues.filter((v, i, arr) => arr.indexOf(v) !== i))];\n\n// Final Result\nreturn [\n {\n json: {\n audit_results:{\n total_nonNumeric_tags: nonNumericValues.length,\n total_kpis_found: kpiMatches.length,\n empty_disclosures: emptyNonNumeric,\n governance_check: governanceTag ? \"PASS\" : \"MISSING\",\n strategy_check: strategyTag ? \"PASS\" : \"MISSING\",\n header_check: headerPresent ? \"PASS\" : \"MISSING\",\n duplicate_disclosures: duplicates,\n }\n\n }\n }\n];\n"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "a16b613e-a7c2-4079-9ff9-46c485019ca3",
|
||||
"name": "AI Agent",
|
||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||
"position": [
|
||||
1240,
|
||||
260
|
||||
],
|
||||
"parameters": {
|
||||
"text": "=Generate an email to the sustainability team summarizing this CSRD XHTML report audit:\n\n{{JSON.stringify($json.audit_results, null, 2)}}\n\nReturn the output in the following JSON format:\n\n{\n \"subject\": \"...\",\n \"body\": \"...\"\n}",
|
||||
"options": {
|
||||
"systemMessage": "=You are LogiGreen CSRD Audit Bot, an ESG compliance assistant writing professional email summaries based on automated XHTML audits for CSRD compliance. Your role is to translate JSON audit results into clear, actionable summaries. Keep a neutral, helpful tone and highlight any risks or missing disclosures. Include key findings and suggest next steps if needed.\n\nWrite emails in plain English with no markdown (avoid **, #, ##, etc.).\nFormat your message with proper line breaks for readability.\nAlways sign with:\nBest regards,\nLogiGreen CSRD Audit Bot"
|
||||
},
|
||||
"promptType": "define",
|
||||
"hasOutputParser": true
|
||||
},
|
||||
"typeVersion": 1.8
|
||||
},
|
||||
{
|
||||
"id": "3dcbaf39-58be-465e-9ec2-0b2a9a8c8fe3",
|
||||
"name": "OpenAI Chat Model",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"position": [
|
||||
1200,
|
||||
420
|
||||
],
|
||||
"parameters": {
|
||||
"model": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gpt-4o-mini"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "6e742627-f315-4ee2-be1b-023b38103978",
|
||||
"name": "Structured Output Parser",
|
||||
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
|
||||
"position": [
|
||||
1500,
|
||||
440
|
||||
],
|
||||
"parameters": {
|
||||
"jsonSchemaExample": "{\n \"subject\": \"CSRD XHTML Report Audit – Key Findings and Next Steps\",\n \"body\": \"Content of the email\"\n}"
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "994e5b98-5bda-4a4f-a3eb-cb521de9d88a",
|
||||
"name": "Reply",
|
||||
"type": "n8n-nodes-base.gmail",
|
||||
"position": [
|
||||
1620,
|
||||
260
|
||||
],
|
||||
"webhookId": "=",
|
||||
"parameters": {
|
||||
"message": "={{ $json.output.body }}",
|
||||
"options": {},
|
||||
"emailType": "text",
|
||||
"messageId": "={{ $('Gmail').item.json.id }}",
|
||||
"operation": "reply"
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 2.1
|
||||
},
|
||||
{
|
||||
"id": "8a7fbdcb-2197-437e-b3ba-126c7942ba4d",
|
||||
"name": "Extract the HTML",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
800,
|
||||
260
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "return [\n {\n json: {\n xhtml_content:$input.first().json.data \n }\n }\n];\n"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "90f271b9-4b8b-49ef-90cc-d10d8e22a203",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
20,
|
||||
-140
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 380,
|
||||
"height": 680,
|
||||
"content": "### 1. Workflow Trigger with Gmail Trigger\nThe workflow is triggered by a new email received in your Gmail mailbox. \nIf the subject includes the string \"CSRD Reporting\" we proceed, if not we do nothing.\n\n#### How to setup?\n- **Gmail Trigger Node:** set up your Gmail API credentials\n[Learn more about the Gmail Trigger Node](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.gmailtrigger)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "803a758c-fba4-4f48-818b-1272c4509e81",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
440,
|
||||
-140
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 640,
|
||||
"height": 680,
|
||||
"content": "### 2. Extract and Process the xHTML report\nThis block extract the attachment file from the email, process the xHTML and perform the audit of the content.\n\n#### How to setup?\n- **Gmail Node:** set up your Gmail API credentials\n[Learn more about the Gmail Trigger Node](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.gmailtrigger)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "0b72f7d8-23ce-4243-b2e5-e3ff5c7f163e",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
1120,
|
||||
-140
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 640,
|
||||
"height": 680,
|
||||
"content": "### 3. AI Agent write and sends an audit report to the send\nThis summarize the results of the analysis in an email sent as a reply to the sender.\n\n#### How to setup?\n- **Gmail Node:** set up your Gmail API credentials\n[Learn more about the Gmail Trigger Node](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.gmailtrigger)\n- **AI Agent with the Chat Model**:\n 1. Add a **chat model** with the required credentials *(Example: Open AI 4o-mini)*\n 2. Adapt the system prompt to the format of emails you want to send\n [Learn more about the AI Agent Node](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "18103fec-6761-4604-872e-dab251211ba0",
|
||||
"name": "HTML from binary",
|
||||
"type": "n8n-nodes-base.extractFromFile",
|
||||
"position": [
|
||||
660,
|
||||
260
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"operation": "text",
|
||||
"binaryPropertyName": "attachment_0"
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "5c31c49d-2324-4d08-a5b5-309925266517",
|
||||
"name": "Email Trigger",
|
||||
"type": "n8n-nodes-base.gmailTrigger",
|
||||
"position": [
|
||||
40,
|
||||
260
|
||||
],
|
||||
"parameters": {
|
||||
"simple": false,
|
||||
"filters": {},
|
||||
"options": {},
|
||||
"pollTimes": {
|
||||
"item": [
|
||||
{
|
||||
"mode": "everyMinute"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "bacbd57d-af9b-49c8-82ae-c74aa2898fc8",
|
||||
"name": "Download Attachment",
|
||||
"type": "n8n-nodes-base.gmail",
|
||||
"position": [
|
||||
480,
|
||||
260
|
||||
],
|
||||
"webhookId": "=",
|
||||
"parameters": {
|
||||
"simple": false,
|
||||
"options": {
|
||||
"downloadAttachments": true
|
||||
},
|
||||
"messageId": "={{ $json.id }}",
|
||||
"operation": "get"
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 2.1
|
||||
},
|
||||
{
|
||||
"id": "af087293-0c3c-4c96-9523-ddb9ed238e00",
|
||||
"name": "Sticky Note3",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
1780,
|
||||
-140
|
||||
],
|
||||
"parameters": {
|
||||
"width": 780,
|
||||
"height": 540,
|
||||
"content": "### 4. Do you need more details?\nFind a step-by-step guide in this tutorial\n\n[🎥 Watch My Tutorial](https://www.youtube.com/watch?v=npeJZv5U7og)"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"AI Agent": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Reply",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Email Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "If",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Check the format": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract the HTML": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Check the format",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"HTML from binary": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract the HTML",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"OpenAI Chat Model": {
|
||||
"ai_languageModel": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent",
|
||||
"type": "ai_languageModel",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Structured Output Parser": {
|
||||
"ai_outputParser": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent",
|
||||
"type": "ai_outputParser",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
*Tags: EU Legislation, Sustainability, Automation, Web Scraping, OpenAI, Google Sheets, Policy Monitoring, Climate*
|
||||
|
||||
### Context
|
||||
|
||||
Hey! I’m Samir, a Supply Chain Engineer and Data Scientist from Paris, and the founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
We use **AI, automation, and data** to support sustainable business practices for small, medium and large companies.
|
||||
|
||||
This workflow is part of our broader initiative to **monitor and act on sustainability legislation** in Europe.
|
||||
|
||||

|
||||
|
||||
> How do you know if new EU laws will impact your business's sustainability goals?
|
||||
|
||||
This n8n workflow automatically scrapes the EU Parliament’s legislative portal to find and flag procedures related to **environmental sustainability**.
|
||||
|
||||
📬 For business inquiries, feel free to connect with me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow is useful for:
|
||||
- **Sustainability consultants** monitoring legal frameworks
|
||||
- **NGOs and researchers** tracking environmental regulations
|
||||
- **Companies** aligning with **CSRD** or **EU Green Deal** objectives
|
||||
- **Policy analysts** looking for automation tools
|
||||
|
||||
### What does it do?
|
||||
|
||||
This n8n workflow:
|
||||
1. 🌐 Scrapes the **EU Parliament legislative portal** for yesterday’s entries
|
||||
2. 🧠 Uses **OpenAI** to classify if each procedure is related to **sustainability**
|
||||
3. 🗂️ Filters out irrelevant items
|
||||
4. 📊 Saves the results in a **Google Sheet**
|
||||
5. ✅ Creates a **Google Task** for each relevant file to review the legislation
|
||||
|
||||
### How it works
|
||||
|
||||

|
||||
|
||||
1. **Trigger manually or on schedule**
|
||||
2. **Scrape HTML blocks** for scheduled debates
|
||||
3. **Parse each procedure** to extract Title, Committee, Rapporteur, PDF link
|
||||
4. **Call GPT-4-turbo** to check if the topic matches sustainability criteria
|
||||
5. **Filter responses** based on “yes” or “no”
|
||||
6. **Store valid items** into Google Sheets
|
||||
7. **Generate tasks** in Google Tasks
|
||||
|
||||
The AI only flags procedures that **directly impact the environment, circular economy, or pollution control**.
|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
You’ll need:
|
||||
- A **Google Sheet** connected to your n8n instance
|
||||
- An **OpenAI account** with GPT-4 access
|
||||
- A **Google Task List**
|
||||
|
||||
### Follow the Guide!
|
||||
Follow the sticky notes in the workflow or **check my tutorial** to configure each node and start using AI to monitor sustainability regulations in Europe.
|
||||
|
||||
[](https://www.youtube.com/watch?v=f_nyArpH6kk)
|
||||
|
||||
🎥 [Watch My Tutorial](https://www.youtube.com/watch?v=f_nyArpH6kk)
|
||||
|
||||
|
||||
### Notes
|
||||
|
||||
- AI filters are strict — you can customise the system prompt to match your needs
|
||||
- This is ideal for tracking **legislative risk** for **climate regulations**
|
||||
|
||||
*This workflow was built using n8n version 1.85.4*
|
||||
*Submitted: April 21, 2025*
|
||||
@@ -0,0 +1,589 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": "="
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "4dfef9cb-d66a-4818-b5b2-6be81f0bd7c3",
|
||||
"name": "Loop Over Items",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"position": [
|
||||
1160,
|
||||
500
|
||||
],
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "3fd73086-62cc-49c4-9c56-b2467a27601c",
|
||||
"name": "Merge",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"position": [
|
||||
1980,
|
||||
360
|
||||
],
|
||||
"parameters": {
|
||||
"mode": "combineBySql"
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "a894cc7b-7e2c-40af-bbdd-de03c9fdf71c",
|
||||
"name": "If",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"position": [
|
||||
2200,
|
||||
440
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 2,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "e3956615-6ad2-4df7-a15f-63f1f21d10fe",
|
||||
"operator": {
|
||||
"name": "filter.operator.equals",
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
},
|
||||
"leftValue": "={{ $json.sustainability }}",
|
||||
"rightValue": "yes"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 2.2
|
||||
},
|
||||
{
|
||||
"id": "b1b1616c-68f7-4911-b58d-8792ac4e822c",
|
||||
"name": "Extract Yesterday Records",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
280,
|
||||
500
|
||||
],
|
||||
"parameters": {
|
||||
"url": "=https://oeil.secure.europarl.europa.eu/oeil/en/search?sessionDay.allDays=false&sessionDay.day={{$now.minus(18,'days').format('yyyyMMdd')}}&sessionDay.type=ALL",
|
||||
"options": {}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "707ae04c-51d3-4547-9868-1c603d359cc0",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1080,
|
||||
"height": 660,
|
||||
"content": "### 1. First Block: scrape the page to extract all the legislative procedures scheduled for debate yesterday\nThis workflow sends an HTTP request to collect the HTML of the page by block. For each block we extract the information of the procedures: **Reference Number**. **Committee**, **Rapporteur**, **Title/Description**, **PDF Link**.\n\n#### How to setup?\n*Nothing to do*\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "721a14b6-c860-431e-8475-b877d5a83768",
|
||||
"name": "Extract HTML Blocks",
|
||||
"type": "n8n-nodes-base.html",
|
||||
"position": [
|
||||
500,
|
||||
500
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"operation": "extractHtmlContent",
|
||||
"extractionValues": {
|
||||
"values": [
|
||||
{
|
||||
"key": "Blocks",
|
||||
"cssSelector": ".erpl_document-wrapper",
|
||||
"returnArray": true,
|
||||
"returnValue": "html"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "fe609066-0f08-40b7-b8a8-13acd8338468",
|
||||
"name": "Parse Blocks",
|
||||
"type": "n8n-nodes-base.html",
|
||||
"position": [
|
||||
720,
|
||||
500
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"operation": "extractHtmlContent",
|
||||
"dataPropertyName": "Blocks",
|
||||
"extractionValues": {
|
||||
"values": [
|
||||
{
|
||||
"key": "Reference Number",
|
||||
"cssSelector": "h3 span.t-item"
|
||||
},
|
||||
{
|
||||
"key": "Committee",
|
||||
"cssSelector": "span.erpl_badge-committee"
|
||||
},
|
||||
{
|
||||
"key": "Rapporteur",
|
||||
"cssSelector": "span.erpl_document-subtitle-author"
|
||||
},
|
||||
{
|
||||
"key": "Title/Description",
|
||||
"cssSelector": "div.erpl_document-body p"
|
||||
},
|
||||
{
|
||||
"key": "PDF Link\t",
|
||||
"attribute": "href",
|
||||
"cssSelector": "a.erpl_document-subtitle-pdf",
|
||||
"returnValue": "attribute"
|
||||
},
|
||||
{
|
||||
"key": "Date",
|
||||
"cssSelector": "div.mt-1 p"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "75770b01-0c98-4077-97d7-3bbc82166372",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
1100,
|
||||
0
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1020,
|
||||
"height": 660,
|
||||
"content": "### 2. Use a LLM to keep only the procedures related to sustainability\nWe loop though all items parsed and we provide the description and the committee to a LLM (Open AI). The LLM will use these information to assess if the procedure is related to **sustainability** or not.\n\n#### How to setup?\n\n- **Open AI Node**:\n 1. Add the required credentials Open AI credentials and select the model *(Example: Open AI 4o-mini)*\n 2. Adapt the system prompt with the topic you want to filter out or keep.\n [Learn more about the AI Agent Node](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "bfdc9844-7d9c-4582-83bb-9e945276864e",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
2140,
|
||||
20
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 500,
|
||||
"height": 660,
|
||||
"content": "### 3. Topics related to sustainability are stored in a Google Sheet\nThe output of the LLM is combined with the other fields. A IF node filters out all the procedure not related to sustainability. The remaining items are loaded in a Google Sheet.\n\n#### How to setup?\n\n- **Record outputs in the Google Sheet Node**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which you want to record your working sessions\n 4. Map the fields: **Reference Number**. **Committee**, **Rapporteur**, **Title/Description**, **PDF Link**\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "38a6d477-0a95-4177-a5d4-10f4c97bcf0c",
|
||||
"name": "Google Tasks",
|
||||
"type": "n8n-nodes-base.googleTasks",
|
||||
"position": [
|
||||
2400,
|
||||
940
|
||||
],
|
||||
"parameters": {
|
||||
"task": "MTIxODU0NDk4MzM3NzAxMTQ0NzY6MDow",
|
||||
"title": "=Study {{ $json['Reference Number'] }} - EU Legislation",
|
||||
"additionalFields": {
|
||||
"notes": "=Title: {{ $json['Title/Description'] }}\nReference Number: {{ $json['Reference Number'] }}\nCommittee: {{ $json.Committee }}\nRapporteur: {{ $json.Rapporteur }}\nPDF Link: {{ $json['PDF Link\t'] }}\nDate: {{ $json.Date }}",
|
||||
"status": "needsAction"
|
||||
}
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "9d27672c-2434-46d3-ae52-e0ba07b3a181",
|
||||
"name": "Sticky Note3",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
2140,
|
||||
700
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 500,
|
||||
"height": 440,
|
||||
"content": "### 4. Create Sustainability Study Task\nCreate a Google Task for each EU legislative file related to sustainability, scheduled for tomorrow at 09:00 AM.\n#### How to setup?\n\n- **Add a task in Google Task**:\n 1. Add your Google Task API credentials to access your task list\n 2. Change the Task List name\n [Learn more about the Google Task Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googletasks)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "8196fd1c-3223-402b-935b-a6a135795999",
|
||||
"name": "When clicking ‘Test workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"position": [
|
||||
60,
|
||||
500
|
||||
],
|
||||
"parameters": {},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "ff6f948b-9db4-479d-afab-3db6176abad6",
|
||||
"name": "Classification Agent",
|
||||
"type": "@n8n/n8n-nodes-langchain.openAi",
|
||||
"position": [
|
||||
1380,
|
||||
280
|
||||
],
|
||||
"parameters": {
|
||||
"modelId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gpt-4-turbo",
|
||||
"cachedResultName": "GPT-4-TURBO"
|
||||
},
|
||||
"options": {},
|
||||
"messages": {
|
||||
"values": [
|
||||
{
|
||||
"content": "=Is the following legislative document related to sustainability? Answer \"yes\" or \"no\".\n\nTitle: {{ $json['Title/Description'] }}\nCommittee: {{ $json[\"Committee\"] }}\n\nBe strict: Only answer \"yes\" if the topic directly impacts environmental or climate sustainability, circular economy, resource conservation, or pollution reduction.\n"
|
||||
},
|
||||
{
|
||||
"role": "system",
|
||||
"content": "Sample output:\n{\"answer\": \"yes\"}\n"
|
||||
}
|
||||
]
|
||||
},
|
||||
"jsonOutput": true
|
||||
},
|
||||
"typeVersion": 1.8
|
||||
},
|
||||
{
|
||||
"id": "01379394-a5e9-4673-bc0e-225e2d3f5214",
|
||||
"name": "Collect Answer",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"position": [
|
||||
1760,
|
||||
280
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "19b1ea4c-3c78-4473-9f16-17d37b273735",
|
||||
"name": "sustainability",
|
||||
"type": "string",
|
||||
"value": "={{ $json.message.content.answer }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 3.4
|
||||
},
|
||||
{
|
||||
"id": "8f96dfd0-0a38-435c-83a0-7649b350f813",
|
||||
"name": "Record Sustainability Procedures",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
2420,
|
||||
380
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"Date": "={{ $json.Date }}",
|
||||
"PDF Link": "={{ $json['PDF Link\t'] }}",
|
||||
"Committee": "={{ $json.Committee }}",
|
||||
"Rapporteur": "={{ $json.Rapporteur }}",
|
||||
"Reference Number": "={{ $json['Reference Number'] }}",
|
||||
"Title/Description": "={{ $json['Title/Description'] }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "Reference Number",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Reference Number",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Committee",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Committee",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Rapporteur",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Rapporteur",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Title/Description",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Title/Description",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "PDF Link",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "PDF Link",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Date",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Date",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "EU Legislative Procedure"
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "=",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "Sustainability Content"
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "rnPYZIig8l6seOd5",
|
||||
"name": "Google Sheets Temporary"
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.5
|
||||
},
|
||||
{
|
||||
"id": "c2cf974e-f182-48f8-9d26-8aea4dbdf486",
|
||||
"name": "Edit Links",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"position": [
|
||||
940,
|
||||
500
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "7a802593-2b9b-42fe-bd0c-66e11510834a",
|
||||
"name": "PDF Link\t",
|
||||
"type": "string",
|
||||
"value": "=https://oeil.secure.europarl.europa.eu{{ $json['PDF Link\t'] }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"includeOtherFields": true
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 3.4
|
||||
},
|
||||
{
|
||||
"id": "bdc398f0-a882-4fbe-ac37-7ca7e15a1081",
|
||||
"name": "Sticky Note4",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
2660,
|
||||
20
|
||||
],
|
||||
"parameters": {
|
||||
"width": 460,
|
||||
"height": 340,
|
||||
"content": "\n[🎥 Check My Tutorial](https://www.youtube.com/watch?v=f_nyArpH6kk)"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"If": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Record Sustainability Procedures",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Google Tasks",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Merge": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "If",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Links": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Google Tasks": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Parse Blocks": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Links",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Collect Answer": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Loop Over Items": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Classification Agent",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract HTML Blocks": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Parse Blocks",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Classification Agent": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Collect Answer",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Yesterday Records": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract HTML Blocks",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Yesterday Records",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
*Tags: Productivity, Pomodoro, Organization*
|
||||
|
||||
### Context
|
||||
Hey! I’m Samir, a Supply Chain Engineer and Data Scientist from Paris, founder of [LogiGreen Consulting](https://logi-green.com) 🌱
|
||||
|
||||
A significant improvement in my productivity came from following the **Pomodoro Technique**.
|
||||
|
||||
### What is the Pomodoro Technique?
|
||||
The Pomodoro Technique is a time management method that breaks your workday into 25-minute focus intervals followed by short breaks. After 4 cycles, you take a longer break to recharge.
|
||||
It helps maintain concentration while preventing burnout.
|
||||
|
||||

|
||||
|
||||
I’ve used this technique with web apps to receive break/work notifications. But I always wished I had a way to **track my sessions** for self-assessment.
|
||||
|
||||
> Let’s use **n8n** to boost our productivity and log our deep work sessions automatically!
|
||||
|
||||
📬 For business inquiries, you can add me on [Here](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who Is This Template For?
|
||||
I built this workflow for creators, freelancers, students, and professionals who love the Pomodoro technique but want more than just timers — they want data.
|
||||
|
||||
This template helps you:
|
||||
- Track every deep work session automatically
|
||||
- Store logs in Google Sheets for later analysis
|
||||
- Stay in control via Telegram commands
|
||||
|
||||
There is no need to pay for premium apps. It’s all **free** and powered by **n8n**.
|
||||
|
||||
### How Does It Work?
|
||||
This Telegram bot tracks your Pomodoro sessions and sends you alerts during the process.
|
||||
|
||||
**Here’s what happens:**
|
||||
|
||||
1. A user sends `/start` to the bot.
|
||||
2. It launches a 25-minute deep work timer.
|
||||
3. After 25 minutes, the bot sends a break reminder.
|
||||
4. **After four cycles**, a long break is triggered and the session is logged.
|
||||
5. The session is **automatically recorded to Google Sheets** with (Date & Time, User ID, Pomodoro count, Session ID, Duration of focus and breaks)
|
||||
|
||||

|
||||
|
||||
### What Gets Tracked?
|
||||
|
||||
| Field | Description |
|
||||
|-------------------|--------------------------------------|
|
||||
| Date & Time | When the session was logged |
|
||||
| User ID | Your Telegram ID |
|
||||
| Block Type | Deep Work or Short Break
|
||||
| Pomodoro Count | Number of cycles completed |
|
||||
| Working Session ID | Unique ID for each session |
|
||||
| Focus Duration | Length of each deep work session |
|
||||
| Break Duration | Short or long break info |
|
||||
|
||||
You can use this workflow as a base to bring additional features like:
|
||||
- Connecting with tasks from **Google Task**
|
||||
- Send automated productivity reports to monitor your activity
|
||||
- Link a Pomodoro with a task using **Google Calendar**
|
||||
|
||||
|
||||
### What Do I Need to Start?
|
||||
|
||||
This workflow is beginner-friendly — **no coding required**.
|
||||
|
||||
- **Google Drive API** and **Google Sheet API** credentials
|
||||
- A Google Sheet set up to log sessions (with the columns of the table above)
|
||||
- API Credentials:
|
||||
- **Google Sheets API** (OAuth2)
|
||||
- **Telegram Bot Token**
|
||||
- Telegram app to chat with the bot
|
||||
|
||||
> The template is plug-and-play. Just follow the sticky notes in the n8n editor to configure it.
|
||||
|
||||
### Next Steps
|
||||
Follow the sticky notes in the n8n workflow editor to:
|
||||
|
||||
- Set your credentials
|
||||
- Connect your Google Sheet
|
||||
- Initialize the static data
|
||||
- Launch your first `/start` command on Telegram
|
||||
|
||||
[](https://www.youtube.com/watch?v=ztMMrmbgGEo)
|
||||
|
||||
🎥 [**Watch My Tutorial**](https://www.youtube.com/watch?v=ztMMrmbgGEo)
|
||||
|
||||
🚀 Curious how n8n can supercharge **productivity and learning skills?**?
|
||||
📬 Let’s connect on [**LinkedIn**](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
This workflow has been created with N8N 1.82.1
|
||||
Submitted: March 24th, 2025
|
||||
@@ -0,0 +1,835 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": "6a5e68bcca67c4cdb3e0b698d01739aea084e1ec06e551db64aeff43d174cb23",
|
||||
"templateCredsSetupCompleted": true
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "bc49829b-45f2-4910-9c37-907271982f14",
|
||||
"name": "Sticky Note3",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
100,
|
||||
0
|
||||
],
|
||||
"parameters": {
|
||||
"width": 780,
|
||||
"height": 540,
|
||||
"content": "### 4. Do you need more details?\nFind a step-by-step guide in this tutorial\n\n[🎥 Watch My Tutorial](https://www.youtube.com/watch?v=ztMMrmbgGEo)"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "b5f24526-f1fc-43b0-82bf-887288838304",
|
||||
"name": "Telegram Trigger",
|
||||
"type": "n8n-nodes-base.telegramTrigger",
|
||||
"position": [
|
||||
-2560,
|
||||
540
|
||||
],
|
||||
"webhookId": "09021985-57be-46c0-ac3d-c3a029ebf9e9",
|
||||
"parameters": {
|
||||
"updates": [
|
||||
"message"
|
||||
],
|
||||
"additionalFields": {}
|
||||
},
|
||||
"typeVersion": 1.1
|
||||
},
|
||||
{
|
||||
"id": "8bb53ae0-515a-493d-9c1a-8a06362ada2e",
|
||||
"name": "If",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"position": [
|
||||
-2340,
|
||||
540
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 2,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "a0be860f-a9ae-4a49-b478-dac25bd550e2",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "startsWith"
|
||||
},
|
||||
"leftValue": "={{ $('Telegram Trigger').item.json.message.text }}",
|
||||
"rightValue": "/"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 2.2
|
||||
},
|
||||
{
|
||||
"id": "2bba3d64-03b7-4875-ac13-d4be13d2aa6d",
|
||||
"name": "Deep Work",
|
||||
"type": "n8n-nodes-base.wait",
|
||||
"position": [
|
||||
-1680,
|
||||
460
|
||||
],
|
||||
"webhookId": "e4e8c51c-286e-47ff-809c-510069debd56",
|
||||
"parameters": {
|
||||
"unit": "minutes",
|
||||
"amount": 25
|
||||
},
|
||||
"typeVersion": 1.1
|
||||
},
|
||||
{
|
||||
"id": "bc3ea269-cbc5-4c7d-8ea1-74413883e425",
|
||||
"name": "Break",
|
||||
"type": "n8n-nodes-base.wait",
|
||||
"position": [
|
||||
-1340,
|
||||
460
|
||||
],
|
||||
"webhookId": "3d3e199b-257c-4517-ab36-3e32242dabf8",
|
||||
"parameters": {
|
||||
"unit": "minutes"
|
||||
},
|
||||
"typeVersion": 1.1
|
||||
},
|
||||
{
|
||||
"id": "da225392-cd09-4afb-ba56-816484c742ae",
|
||||
"name": "Initiate Static Data",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"notes": "You only need to run the initialization step once per workflow, regardless of the number of Telegram chat IDs. The initialization creates the telegramStates object within the global static data of the workflow. Once that object exists, the workflow will use it to store the state for any chat ID.",
|
||||
"position": [
|
||||
-2540,
|
||||
-160
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "let workflowStaticData = $getWorkflowStaticData('global');\nif (!workflowStaticData.telegramStates) {\n workflowStaticData.telegramStates = {}; \n}\nreturn workflowStaticData;\n"
|
||||
},
|
||||
"notesInFlow": false,
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "b4f1ae18-ff8d-4b1d-8c26-8e3f87350d2d",
|
||||
"name": "Increment Count",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
-1180,
|
||||
460
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "let workflowStaticData = $getWorkflowStaticData('global');\n\nif (!workflowStaticData.telegramStates) {\n workflowStaticData.telegramStates = {}; \n}\n\nlet userId = $('Telegram Trigger').first().json.message.chat.id.toString();\n\n// Ensure the user object exists\nif (!workflowStaticData.telegramStates[userId]) {\n workflowStaticData.telegramStates[userId] = { count: 0, sessionId: \"\", startTime: \"\" };\n}\n\n// Check if sessionId is missing, then generate one\nif (!workflowStaticData.telegramStates[userId].sessionId) {\n workflowStaticData.telegramStates[userId].sessionId = Date.now().toString(36) + Math.random().toString(36).substring(2, 8);\n workflowStaticData.telegramStates[userId].startTime = new Date().toISOString();\n}\n\n// Increment the Pomodoro count\nworkflowStaticData.telegramStates[userId].count += 1;\n\n// Return the updated session details\nreturn [\n {\n json: {\n count: workflowStaticData.telegramStates[userId].count,\n sessionId: workflowStaticData.telegramStates[userId].sessionId,\n startTime: workflowStaticData.telegramStates[userId].startTime\n }\n }\n];\n"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "5315d3fc-9c1e-4f3d-9e00-81f806d84e6a",
|
||||
"name": "Record Deep Work",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
-720,
|
||||
400
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"Date": "={{ $now.format('dd-LL-yyyy') }}",
|
||||
"Time": "={{ $now.hour.toString().padStart(2, '0') }}:{{ $now.minute.toString().padStart(2, '0') }} ",
|
||||
"User ID": "={{ $json.result.chat.id }}",
|
||||
"Block Type": "Deep Work",
|
||||
"Pomodoro Count": "={{ $('Increment Count').item.json.count }}",
|
||||
"Working Session ID": "={{ $('Increment Count').item.json.sessionId }}",
|
||||
"Break Duration (min)": "5",
|
||||
"Focus Duration (min)": "25"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "Date",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Date",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Time",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Time",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "User ID",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "User ID",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Working Session ID",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "Working Session ID",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Block Type",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "Block Type",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Pomodoro Count",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Pomodoro Count",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Focus Duration (min)",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Focus Duration (min)",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Break Duration (min)",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "Break Duration (min)",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "=",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.5
|
||||
},
|
||||
{
|
||||
"id": "7328bccb-bb00-42bb-9659-6f66600894cc",
|
||||
"name": "Record Long Break",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
-120,
|
||||
460
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"Date": "={{ $now.format('dd-LL-yyyy') }}",
|
||||
"Time": "={{ $now.hour.toString().padStart(2, '0') }}:{{ $now.minute.toString().padStart(2, '0') }} ",
|
||||
"User ID": "={{ $json.result.chat.id }}",
|
||||
"Block Type": "Long Break",
|
||||
"Pomodoro Count": "={{ $('Increment Count').item.json.count }}",
|
||||
"Working Session ID": "={{ $('Increment Count').item.json.sessionId }}",
|
||||
"Break Duration (min)": "15",
|
||||
"Focus Duration (min)": "0"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "Date",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Date",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Time",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Time",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "User ID",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "User ID",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Working Session ID",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "Working Session ID",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Block Type",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "Block Type",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Pomodoro Count",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Pomodoro Count",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Focus Duration (min)",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Focus Duration (min)",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Break Duration (min)",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "Break Duration (min)",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "=",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
}
|
||||
},
|
||||
"typeVersion": 4.5
|
||||
},
|
||||
{
|
||||
"id": "2d45a378-7188-4ac2-bb2e-c0ba745794d7",
|
||||
"name": "Instructions Message",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"position": [
|
||||
-2160,
|
||||
660
|
||||
],
|
||||
"webhookId": "a4c1043e-0520-4ef6-994c-6e733f90827b",
|
||||
"parameters": {
|
||||
"text": "=💡 Oops! That’s not a valid command.\n\nHere’s what you can do:\n✅ /start – Kick off a Pomodoro session and get in the zone.\n✅ /stop – Wrap up your session like a productivity pro.\n\nNow, let’s get some deep work done! 🔥💻",
|
||||
"chatId": "={{ $('Telegram Trigger').item.json.message.from.id }}",
|
||||
"additionalFields": {}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "14c90858-f22d-4495-bda0-c846209e6736",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-2580,
|
||||
-400
|
||||
],
|
||||
"parameters": {
|
||||
"width": 440,
|
||||
"height": 380,
|
||||
"content": "### 0. Initiate Workplace Static Data\nRun it **once** before activating the workflow to initialize workspace data that will be used to **store state flags** and **outputs from users**.\n\n#### How to setup?\n- **Code Node:** do not change anything, just run it\n [Learn more about the code node](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.code)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "26cdb973-26eb-41a6-b071-11b5b51faf70",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-2580,
|
||||
0
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 620,
|
||||
"height": 940,
|
||||
"content": "### 1. Workflow Trigger with Telegram Message\n1. The workflow is triggered by a user message. \n2. The second is checking if the message is a command (starting with \"/\") to route it to the proper block. If the message is not a command, the bot sends an instruction message to the user.\n3. The third node checks if the message is a **/stop**. If yes, we stop the workflow the bot send a notice to the user and state variables are clears\n\n#### How to setup?\n- **Telegram Nodes:** set up your telegram bot credentials\n[Learn more about the Telegram Trigger Node](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.telegramtrigger/)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "6d4f131c-076e-4d0a-9738-847692557468",
|
||||
"name": "start or stop?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"position": [
|
||||
-2160,
|
||||
460
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 2,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "f169eadd-424b-42b0-8229-615608ecb23c",
|
||||
"operator": {
|
||||
"name": "filter.operator.equals",
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
},
|
||||
"leftValue": "={{ $('Telegram Trigger').item.json.message.text }}",
|
||||
"rightValue": "/start"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 2.2
|
||||
},
|
||||
{
|
||||
"id": "aa50b802-83f1-47da-a9eb-cf2fc9576c28",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-1900,
|
||||
0
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1360,
|
||||
"height": 940,
|
||||
"content": "### 2. Deep Work Blocks of 25 minutes\n1. The bot notifies the user that the session started.\n2. After 25 minutes, it sends a notification to inform the user that [he/she] should take a break.\n3. The loop continues until we reached four working sessions.\n\n#### Why do we need Google Sheets?\nEach deep work session is recorded to help users keep track of their stats.\n\n#### How to setup?\n- **Deep Work & Break Mode**: fix the amount of time you want for the deep work session (Default: 25 min) and the short break (Default: 5 min)\n- **Telegram Nodes:** set up your telegram bot credentials\n[Learn more about the Telegram Trigger Node](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.telegramtrigger/)\n- **Record Deep Work in the Google Sheet Node**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which you want to record your working sessions\n 4. Map the fields\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "6a7b7b74-61b0-40de-8def-7bc602302d05",
|
||||
"name": "Long Break Notification",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"position": [
|
||||
-440,
|
||||
520
|
||||
],
|
||||
"webhookId": "251e850d-fcad-4fe8-a335-001ff1677415",
|
||||
"parameters": {
|
||||
"text": "🍴 Time for a long break. Great job!",
|
||||
"chatId": "={{ $('Telegram Trigger').item.json.message.from.id }}",
|
||||
"additionalFields": {
|
||||
"appendAttribution": false
|
||||
}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "fc63c241-53ee-4443-9a8c-a8f8a5da992b",
|
||||
"name": "Clear Variables1",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
-1680,
|
||||
760
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "let workflowStaticData = $getWorkflowStaticData('global');\nif (workflowStaticData.telegramStates) {\n delete workflowStaticData.telegramStates[$('Telegram Trigger').first().json.message.chat.id.toString()];\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "12297308-57ca-493f-8e65-49e396042893",
|
||||
"name": "Clear Variables2",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
-120,
|
||||
640
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "let workflowStaticData = $getWorkflowStaticData('global');\nif (workflowStaticData.telegramStates) {\n delete workflowStaticData.telegramStates[$('Telegram Trigger').first().json.message.chat.id.toString()];\n}\n\nreturn $input.all();"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "46ab1b8b-c9be-4da9-a470-d1a4c9fcc219",
|
||||
"name": "< 4 Cycles",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"position": [
|
||||
-720,
|
||||
540
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 2,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "307c1ccf-9a10-49e6-a59d-d250edb1cae5",
|
||||
"operator": {
|
||||
"type": "number",
|
||||
"operation": "gt"
|
||||
},
|
||||
"leftValue": "={{ $('Increment Count').item.json.count }}",
|
||||
"rightValue": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 2.2
|
||||
},
|
||||
{
|
||||
"id": "22752905-50fc-48ee-9b4c-f80e6184c17f",
|
||||
"name": "Short Break Notification",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"position": [
|
||||
-1520,
|
||||
460
|
||||
],
|
||||
"webhookId": "b436cc4e-83d8-4bfa-9de9-e37cde83f9f9",
|
||||
"parameters": {
|
||||
"text": "🚰 Work session complete! Take a short break.",
|
||||
"chatId": "={{ $('Telegram Trigger').item.json.message.from.id }}",
|
||||
"additionalFields": {
|
||||
"appendAttribution": false
|
||||
}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "d43993be-ae96-4f83-8047-fffc956d2bac",
|
||||
"name": "Back to Work Notification",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"position": [
|
||||
-1000,
|
||||
460
|
||||
],
|
||||
"webhookId": "d13ad958-abf2-46a8-8785-da369933de24",
|
||||
"parameters": {
|
||||
"text": "=🏢 Break over! Back to work for the cycle: {{ $json.count }}",
|
||||
"chatId": "={{ $('Telegram Trigger').item.json.message.from.id }}",
|
||||
"additionalFields": {
|
||||
"appendAttribution": false
|
||||
}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "480f9003-3c1d-44a1-8552-8b59d9d074af",
|
||||
"name": "Start Cycle Notification",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"position": [
|
||||
-1840,
|
||||
460
|
||||
],
|
||||
"webhookId": "16fa6143-2d4e-44b5-b0d0-3d58bc6022a8",
|
||||
"parameters": {
|
||||
"text": "⏰ Time to focus! 25 minutes of deep work starts now.",
|
||||
"chatId": "={{ $('Telegram Trigger').item.json.message.from.id }}",
|
||||
"additionalFields": {
|
||||
"appendAttribution": false
|
||||
}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "b0ce8b4c-7e41-47f6-b505-46fdf606b84d",
|
||||
"name": "End of Session Notification",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"position": [
|
||||
-1860,
|
||||
760
|
||||
],
|
||||
"webhookId": "126e70c5-ea40-4bd5-81ee-5ca459db6a0d",
|
||||
"parameters": {
|
||||
"text": "=🛑 You decided to stop the session early.\n🚀 Use /start to relaunch a working session.",
|
||||
"chatId": "={{ $('Telegram Trigger').item.json.message.from.id }}",
|
||||
"additionalFields": {
|
||||
"appendAttribution": false
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"retryOnFail": false,
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "bd6b313b-2418-4b03-ad0e-7373b26b68c3",
|
||||
"name": "Sticky Note4",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-500,
|
||||
0
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 560,
|
||||
"height": 940,
|
||||
"content": "### 3. End of the session for a long break\n1. The bot notifies the user that the session ended.\n2. The long break is recorded in the Google Sheets.\n3. Variables are cleared so the workflow is ready for a new session with this user.\n\n#### How to setup?\n- **Telegram Nodes:** set up your telegram bot credentials\n[Learn more about the Telegram Trigger Node](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.telegramtrigger/)\n- **Record Long Break in the Google Sheet Node**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which you want to record your working sessions\n 4. Map the fields\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Telegram Trigger": [
|
||||
{
|
||||
"message": {
|
||||
"chat": {
|
||||
"id": 0,
|
||||
"type": "private",
|
||||
"username": "=",
|
||||
"first_name": "="
|
||||
},
|
||||
"date": 1742551547,
|
||||
"from": {
|
||||
"id": 0,
|
||||
"is_bot": false,
|
||||
"username": "=",
|
||||
"first_name": "=",
|
||||
"language_code": "en"
|
||||
},
|
||||
"text": "/stop",
|
||||
"entities": [
|
||||
{
|
||||
"type": "bot_command",
|
||||
"length": 6,
|
||||
"offset": 0
|
||||
}
|
||||
],
|
||||
"message_id": 1846
|
||||
},
|
||||
"update_id": 567456699
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"If": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "start or stop?",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Instructions Message",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Break": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Increment Count",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Deep Work": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Short Break Notification",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"< 4 Cycles": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Long Break Notification",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Start Cycle Notification",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"start or stop?": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Start Cycle Notification",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "End of Session Notification",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Increment Count": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Back to Work Notification",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Telegram Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "If",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Long Break Notification": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Clear Variables2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Record Long Break",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Short Break Notification": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Break",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Start Cycle Notification": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Deep Work",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Back to Work Notification": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "< 4 Cycles",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Record Deep Work",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"End of Session Notification": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Clear Variables1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
*Tags: Scrapping, Events, European Union, Networking*
|
||||
|
||||
### Context
|
||||
|
||||
Hey! I’m Samir, a Supply Chain Engineer and Data Scientist from Paris, and the founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
We use **AI, automation, and data** to support sustainable and data-driven operations across all types of organizations.
|
||||
|
||||
This workflow is part of our networking strategy (as a business) to **track official EU events** that may relate to topics we cover.
|
||||
|
||||

|
||||
|
||||
> Want to stay ahead of critical EU meetings and events without checking the website every day?
|
||||
|
||||
This n8n workflow automatically scrapes the **EU’s official event portal** and logs the latest entries with clean metadata including date, location, category, and link.
|
||||
|
||||
📬 For collaborations, feel free to connect with me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow is useful for:
|
||||
- **Policy & public affairs teams** following institutional activities
|
||||
- **Sustainability teams** watching for relevant climate-related summits
|
||||
- **NGOs and researchers** interested in event calendars
|
||||
- **Data teams** building dashboards on public event trends
|
||||
|
||||
### What does it do?
|
||||
|
||||
This n8n workflow:
|
||||
1. 🌐 Scrapes the **EU events portal** for new meetings and conferences
|
||||
2. 📅 Extracts event metadata (title, date, location, type, and link)
|
||||
3. 🔁 Handles **pagination** across multiple pages
|
||||
4. 🚫 Checks for **duplicates** already stored
|
||||
5. 📊 Saves new records into a connected **Google Sheet**
|
||||
|
||||
### How it works
|
||||
|
||||

|
||||
|
||||
1. **Triggered daily via cron**
|
||||
2. **HTTP node loads the event listing HTML**
|
||||
3. **Extract HTML blocks** for each event article
|
||||
4. **Parse event name, link, type, location, and full date**
|
||||
5. **Concatenate and clean dates** for easy tracking
|
||||
6. **Store non-duplicate entries** in Google Sheets
|
||||
|
||||
The workflow uses static data to track pagination and ensure only **new events are stored**, making it ideal for building up a clean dataset over time.
|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
You’ll need:
|
||||
- A **Google Sheet** connected to your n8n instance
|
||||
- No code or AI tools needed — just n8n and this template
|
||||
|
||||
### Follow the Guide!
|
||||
|
||||
Sticky notes are included directly inside the workflow to guide you step-by-step through setup and customisation.
|
||||
|
||||
[](https://www.youtube.com/watch?v=ngGB2yEPPRk)
|
||||
|
||||
[🎥 Watch My Tutorial](https://www.youtube.com/watch?v=ngGB2yEPPRk)
|
||||
|
||||
### Notes
|
||||
|
||||
- This is ideal for analysts and consultants who want **clean, structured data** from the EU portal
|
||||
- You can add filtering, email alerts, or AI classifiers later
|
||||
|
||||
*This workflow was built using n8n version 1.93.0*
|
||||
*Submitted: June 1, 2025*
|
||||
@@ -0,0 +1,743 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": ""
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "c9c69735-c28f-4d7a-bc1c-e3080775afee",
|
||||
"name": "Initiate Static Data",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"notes": "You only need to run the initialization step once per workflow, regardless of the number of Telegram chat IDs. The initialization creates the telegramStates object within the global static data of the workflow. Once that object exists, the workflow will use it to store the state for any chat ID.",
|
||||
"position": [
|
||||
260,
|
||||
240
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "let workflowStaticData = $getWorkflowStaticData('global');\nworkflowStaticData.page = -1;\nworkflowStaticData.results = [];\nreturn workflowStaticData;\n"
|
||||
},
|
||||
"notesInFlow": false,
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "5bdf8d01-a9da-4c31-b13a-92e1f39e3e37",
|
||||
"name": "Store Tables",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
1320,
|
||||
200
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "const workflowStaticData = $getWorkflowStaticData('global');\n\nif (!Array.isArray(workflowStaticData.results)) {\n workflowStaticData.results = [];\n}\n\nconst newEvents = $input.all().map(item => item.json);\n\nworkflowStaticData.results.push(...newEvents);\n\nreturn [\n {\n json: {\n addedThisRound: newEvents.length,\n totalStored: workflowStaticData.results.length\n }\n }\n];\n"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "4556022a-7ba7-47f4-bf52-9f9a14a3e22b",
|
||||
"name": "page+1",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
260,
|
||||
400
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "let workflowStaticData = $getWorkflowStaticData('global');\n\nif (!workflowStaticData.page) {\n workflowStaticData.page = 0; \n}\nworkflowStaticData.page += 1\n\nreturn {\n 'page': workflowStaticData.page\n}"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "8c993d99-7332-46eb-a08b-a8bdc4379294",
|
||||
"name": "If",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"position": [
|
||||
1320,
|
||||
400
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 2,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "or",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "315735a1-8c34-4c9f-a4ff-a6e9bd715d13",
|
||||
"operator": {
|
||||
"type": "number",
|
||||
"operation": "gt"
|
||||
},
|
||||
"leftValue": "={{ $('page+1').item.json.page }}",
|
||||
"rightValue": "={{ 3 }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 2.2
|
||||
},
|
||||
{
|
||||
"id": "b99bb32a-40bd-40b4-9515-79ea1a8e834f",
|
||||
"name": "Extract Blocks",
|
||||
"type": "n8n-nodes-base.html",
|
||||
"position": [
|
||||
660,
|
||||
400
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"operation": "extractHtmlContent",
|
||||
"extractionValues": {
|
||||
"values": [
|
||||
{
|
||||
"key": "blocks",
|
||||
"cssSelector": "article.ecl-content-item",
|
||||
"returnArray": true,
|
||||
"returnValue": "html"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "228e0085-0f55-4684-831f-561ea9e8b21a",
|
||||
"name": "Query EU Website",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
480,
|
||||
400
|
||||
],
|
||||
"parameters": {
|
||||
"url": "=https://european-union.europa.eu/news-and-events/events_en?page={{ $json.page }}",
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "b0903872-0698-42d0-a26f-7b173e5b675a",
|
||||
"name": "Load Old Records",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
460,
|
||||
920
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.6
|
||||
},
|
||||
{
|
||||
"id": "2ca706a4-8dcf-4e7d-a403-af485768d30f",
|
||||
"name": "Aggregate",
|
||||
"type": "n8n-nodes-base.aggregate",
|
||||
"position": [
|
||||
680,
|
||||
920
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"fieldsToAggregate": {
|
||||
"fieldToAggregate": [
|
||||
{
|
||||
"renameField": true,
|
||||
"outputFieldName": "events",
|
||||
"fieldToAggregate": "event_name"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "e69edaf8-0ad9-475e-8d3b-89ab12adfbc8",
|
||||
"name": "Events Already Existing?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"position": [
|
||||
1260,
|
||||
920
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 2,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "b69cc214-26d5-4a38-aee0-68d4f556c87a",
|
||||
"operator": {
|
||||
"type": "array",
|
||||
"operation": "contains",
|
||||
"rightType": "any"
|
||||
},
|
||||
"leftValue": "={{ $json.events }}",
|
||||
"rightValue": "={{ $json.event_name }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 2.2
|
||||
},
|
||||
{
|
||||
"id": "5b181fad-b08c-47d9-83e3-3cec229ab7d3",
|
||||
"name": "15 sec",
|
||||
"type": "n8n-nodes-base.wait",
|
||||
"position": [
|
||||
1580,
|
||||
480
|
||||
],
|
||||
"webhookId": "77ab0eb9-d29a-47ea-a076-35930247e9bb",
|
||||
"parameters": {
|
||||
"amount": 15
|
||||
},
|
||||
"typeVersion": 1.1
|
||||
},
|
||||
{
|
||||
"id": "180d5c28-6c37-4b99-bdf2-8a5dd1e1eaf5",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 400,
|
||||
"height": 680,
|
||||
"content": "### 1. Workflow Trigger with Cron Job\nThe workflow is triggered every morning at 08:30 am (local time). It starts with the initialization of variables that will store **page number** and **events informations**.\n\n#### How to setup?\n- Select the time you want to set it up\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "7a78eb03-8c2e-4c83-8837-85714e7ee192",
|
||||
"name": "Schedule Trigger",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"position": [
|
||||
20,
|
||||
340
|
||||
],
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [
|
||||
{
|
||||
"triggerAtHour": 8
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "8157f612-0a1d-4b1d-bf1b-045bfd54f57c",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
420,
|
||||
0
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1340,
|
||||
"height": 680,
|
||||
"content": "### 2. Scrapping and Parsing of Events blocks\nThis starts with the HTTP node collecting HTML code that is parsed to extract Event description, link, date and location.\n#### How to setup?\n- Set up the number of pages to scrape: fixed at 5 pages (default)\n- Set up the waiting time between two pages scrapped to avoid overloading the server"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "653dcf5a-4cc6-4c44-90d1-2d867fcb9584",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
420,
|
||||
700
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 580,
|
||||
"height": 500,
|
||||
"content": "### 3. Load events recorded in the Google Sheet\nLoading the events **already scrapped** to avoid duplicates.\n#### How to setup?\n1. Input of your **Google Sheet** credentials\n2. Select the folder and file where the events are stored\n3. Map the fields: **event_name, event_link, day, month, year\tevent_type, event_location**\n[Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n\n "
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "fcf9b90c-3910-4452-a9f6-4c0a659c2696",
|
||||
"name": "Collect Fields",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"position": [
|
||||
1120,
|
||||
400
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "c446f4e4-380d-488b-af58-49d10d86b9ef",
|
||||
"name": "event_name",
|
||||
"type": "string",
|
||||
"value": "={{ $json.event_name }}"
|
||||
},
|
||||
{
|
||||
"id": "d96c9c62-ce40-48bd-96eb-8365de6b2a0e",
|
||||
"name": "event_link",
|
||||
"type": "string",
|
||||
"value": "={{ $json.event_link }}"
|
||||
},
|
||||
{
|
||||
"id": "f7bfa3be-6cb4-4577-8047-ca6c8f299b3c",
|
||||
"name": "day",
|
||||
"type": "string",
|
||||
"value": "={{ $json.day }}"
|
||||
},
|
||||
{
|
||||
"id": "cc6e4317-d685-436b-bccf-90d577a35aa1",
|
||||
"name": "month",
|
||||
"type": "string",
|
||||
"value": "={{ $json.month_1 || $json.month_2 }}"
|
||||
},
|
||||
{
|
||||
"id": "88f3ab71-6a9a-4e14-9f85-070637bc8a67",
|
||||
"name": "year",
|
||||
"type": "string",
|
||||
"value": "={{ $json.year }}"
|
||||
},
|
||||
{
|
||||
"id": "7e9247e5-350c-4813-96cc-d6e849324f55",
|
||||
"name": "event_type",
|
||||
"type": "string",
|
||||
"value": "={{ $json.event_type }}"
|
||||
},
|
||||
{
|
||||
"id": "d2bb0c0d-4d3b-49c8-846f-857a29063701",
|
||||
"name": "event_location",
|
||||
"type": "string",
|
||||
"value": "={{ $json.event_location }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 3.4
|
||||
},
|
||||
{
|
||||
"id": "85128970-d6d9-44d2-9633-1bf2aa2dccb5",
|
||||
"name": "Parse Information",
|
||||
"type": "n8n-nodes-base.html",
|
||||
"position": [
|
||||
880,
|
||||
400
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"operation": "extractHtmlContent",
|
||||
"dataPropertyName": "blocks",
|
||||
"extractionValues": {
|
||||
"values": [
|
||||
{
|
||||
"key": "event_name",
|
||||
"cssSelector": "div.ecl-content-block__title a"
|
||||
},
|
||||
{
|
||||
"key": "event_link",
|
||||
"attribute": "href",
|
||||
"cssSelector": "div.ecl-content-block__title a",
|
||||
"returnValue": "attribute"
|
||||
},
|
||||
{
|
||||
"key": "day",
|
||||
"cssSelector": "span.ecl-date-block__day"
|
||||
},
|
||||
{
|
||||
"key": "month_1",
|
||||
"attribute": "title",
|
||||
"cssSelector": "abbr.ecl-date-block__month",
|
||||
"returnValue": "attribute"
|
||||
},
|
||||
{
|
||||
"key": "year",
|
||||
"cssSelector": "span.ecl-date-block__year"
|
||||
},
|
||||
{
|
||||
"key": "event_type",
|
||||
"cssSelector": "li.ecl-content-block__primary-meta-item"
|
||||
},
|
||||
{
|
||||
"key": "event_location",
|
||||
"cssSelector": ".ecl-content-block__description li"
|
||||
},
|
||||
{
|
||||
"key": "month_2",
|
||||
"cssSelector": "span.ecl-date-block__month"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "97a911da-4ab1-4cf5-b1c0-33361230c2cd",
|
||||
"name": "Combine New + Old Records",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"position": [
|
||||
1060,
|
||||
920
|
||||
],
|
||||
"parameters": {
|
||||
"mode": "combineBySql"
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "d96ac76d-306d-4f41-b394-c8461652067c",
|
||||
"name": "Store New Records",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
1520,
|
||||
940
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"day": "={{ $json.day }}",
|
||||
"year": "={{ $json.year }}",
|
||||
"month": "={{ $json.month }}",
|
||||
"event_link": "={{ $json.event_link }}",
|
||||
"event_name": "={{ $json.event_name }}",
|
||||
"event_type": "={{ $json.event_type }}",
|
||||
"event_location": "={{ $json.event_location }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "event_name",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "event_name",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "event_link",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "event_link",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "day",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "day",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "month",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "month",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "year",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "year",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "event_type",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "event_type",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "event_location",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "event_location",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.6
|
||||
},
|
||||
{
|
||||
"id": "c5167368-cb0c-434a-8cf2-a617e34a8c1f",
|
||||
"name": "Sticky Note3",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
1020,
|
||||
700
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 740,
|
||||
"height": 400,
|
||||
"content": "### 4. Record New Events in the Google Sheet\nRecording of new events in the google sheet.\n#### How to setup?\n1. Input of your **Google Sheet** credentials\n2. Select the folder and file where the events are stored\n3. Map the fields: **Reference Number**. **Committee**, **Rapporteur**, **Title/Description**, **PDF Link**\n[Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n\n "
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "caa2365c-ce53-4617-847e-fef99c3b0b35",
|
||||
"name": "Return Lines Scrapped",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
560,
|
||||
1060
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "const workflowStaticData = $getWorkflowStaticData('global');\n\nif (!Array.isArray(workflowStaticData.results)) {\n return [];\n}\n\nreturn workflowStaticData.results.map(result => ({ json: result }));\n"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "37011622-32fc-474d-97ac-8ce01efbe44f",
|
||||
"name": "Sticky Note4",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-180,
|
||||
700
|
||||
],
|
||||
"parameters": {
|
||||
"width": 580,
|
||||
"height": 380,
|
||||
"content": "### [Check the Tutorial](https://www.youtube.com/watch?v=ngGB2yEPPRk)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"If": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "15 sec",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"15 sec": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "page+1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"page+1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Query EU Website",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Aggregate": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Combine New + Old Records",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Collect Fields": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Store Tables",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "If",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Blocks": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Parse Information",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Load Old Records": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Aggregate",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Query EU Website": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Blocks",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Schedule Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "page+1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Initiate Static Data",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Load Old Records",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Return Lines Scrapped",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Parse Information": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Collect Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Return Lines Scrapped": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Combine New + Old Records",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Events Already Existing?": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Store New Records",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Combine New + Old Records": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Events Already Existing?",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
*Tags: Automation, Finance, Google Sheets, API*
|
||||
|
||||
*Note: This workflow uses the Exchange rate API and requires a valid API key.*
|
||||
|
||||
### Context
|
||||
I’m a **Supply Chain Data Scientist** who builds automations to streamline operations, reduce manual tasks, and boost decision-making through real-time data.
|
||||
|
||||
In this workflow, I automated the task of **fetching live currency exchange rates**, updating a **Google Sheet** with the latest values, and **archiving historical records** — all without writing any code.
|
||||
|
||||
> Improve your productivity by automating admin tasks with n8n!
|
||||
|
||||
📬 For business inquiries, you can add me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
This template is perfect for:
|
||||
- **Finance teams** tracking multi-currency cashflows
|
||||
- **Analysts** building dashboards or models requiring updated FX data
|
||||
- **Anyone working with spreadsheets** who needs up-to-date exchange rates
|
||||
|
||||
It updates:
|
||||
- A live sheet with the latest USD-based exchange rates
|
||||
- An archive tab to track historical changes over time
|
||||
|
||||

|
||||
|
||||
### How does it work?
|
||||
This workflow runs in N8N and performs the following steps:
|
||||
|
||||
1. 🌐 **Calls the ExchangeRate API** to get the latest rates based on USD
|
||||
2. 🧠 **Extracts and formats key fields**: base currency, timestamp, and conversion values
|
||||
3. 📊 **Updates a main Google Sheet** with the latest data (using upsert logic)
|
||||
4. 🗂️ **Appends all rates** to a second Google Sheet tab for historical tracking
|
||||
|
||||
You can schedule this workflow to run daily, hourly, or on-demand.
|
||||
|
||||
### What do I need to start?
|
||||
You don’t need to write a single line of code.
|
||||
#### Prerequisites:
|
||||
- A **Google Sheet** with two tabs: `Rate Sheet` and `Archives` *(Link of the publicly available example in the template)*
|
||||
- A **valid Exchangerate API key**
|
||||
- **Google Sheets API** connected via OAuth2
|
||||
|
||||
### Next Steps
|
||||
Use the sticky notes in the workflow to understand how to:
|
||||
- Add your Exchangerate API key
|
||||
- Map the fields to match your Google Sheet layout
|
||||
- Schedule the run frequency using the **Cron node**
|
||||
- Optionally add Slack or email alerts if the base rate changes
|
||||
|
||||
For more information, check my tutorial:
|
||||
|
||||
[](https://www.youtube.com/watch?v=Lhi6hV6rWEo)
|
||||
|
||||
[🎥 Watch My Tutorial](https://www.youtube.com/watch?v=Lhi6hV6rWEo)
|
||||
|
||||
🚀 Want to build finance automation workflows like this?
|
||||
📬 Let’s connect on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Notes
|
||||
- You can adapt this template for **other currencies** by changing the API endpoint
|
||||
|
||||
|
||||
*This workflow was built using **n8n 1.85.4**
|
||||
Submitted: April 15th, 2025*
|
||||
+3428
File diff suppressed because it is too large.
Load diff
+68
@@ -0,0 +1,68 @@
|
||||
**\*Tags**: Crypto, Currency Exchange, Alpha Vantage API, Google Sheets\*
|
||||
|
||||
### Context
|
||||
|
||||
Hi! I’m Samir — a Supply Chain Engineer and Data Scientist based in Paris, and founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
I help companies automate data pipelines using **APIs**, **AI agents**, and **workflow automation** to improve operational visibility and decision-making.
|
||||
|
||||
This workflow is part of our **financial monitoring toolkit**, allowing businesses to **track real-time cryptocurrency exchange rates** for assets like BTC and ETH.
|
||||
|
||||
> Monitor crypto rates in real time and log the data to Google Sheets.
|
||||
|
||||
📬 For business inquiries, feel free to connect with me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow is designed for **crypto analysts**, **finance teams**, or **automation enthusiasts** who want to collect and track crypto exchange rates automatically for reporting or analysis.
|
||||
|
||||
Let’s imagine you want to monitor BTC and ETH prices against the EUR:
|
||||
|
||||

|
||||
|
||||
The **Alpha Vantage API** is called every hour to collect the exchange rate, bid/ask prices, and timestamp.
|
||||
|
||||
This data is saved in **Google Sheets** and can trigger a **Telegram alert**.
|
||||
|
||||
### How does it work?
|
||||
|
||||
This workflow automates the hourly logging of crypto exchange rates:
|
||||
|
||||
- ⏰ Schedule Trigger launches the workflow every hour
|
||||
- 🌐 Alpha Vantage API fetches BTC and ETH exchange rates (to EUR)
|
||||
- 📄 Google Sheets node appends each result to a tab (BTC or ETH)
|
||||
- 📲 Telegram node sends a summary alert with the latest rate info
|
||||
|
||||
### Steps:
|
||||
|
||||
1. ⏰ Trigger every hour via Schedule node
|
||||
2. 🌐 Call Alpha Vantage API for BTC and ETH
|
||||
3. 📊 Append rates, bid/ask prices, and timestamp to Google Sheets
|
||||
4. 📲 Send a formatted Telegram message with rate summary
|
||||
|
||||

|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
You’ll need:
|
||||
|
||||
- A free [Alpha Vantage API key](https://www.alphavantage.co/support/#api-key)
|
||||
- A **Google Sheet** with BTC and ETH tabs
|
||||
- A **Telegram bot** and Chat ID to send notifications
|
||||
|
||||
### Next Steps
|
||||
|
||||
🗒️ Use the sticky notes in the n8n canvas to:
|
||||
|
||||
- Add your Alpha Vantage API key and Google Sheet credentials
|
||||
- Set your Telegram Chat ID and bot connection
|
||||
- Monitor and visualise real-time crypto prices with no manual input
|
||||
|
||||
### 📺 Learn More with n8n Tutorials
|
||||
|
||||
Get hands-on experience with automation through my comprehensive **n8n tutorial playlist**, featuring 18+ complete, step-by-step videos and ready-to-use templates.
|
||||
|
||||
[](https://bit.ly/playlist-n8n-supplyscience)
|
||||
|
||||
_This template was built using n8n v1.93.0_
|
||||
_Submitted: June 12, 2025_
|
||||
+568
@@ -0,0 +1,568 @@
|
||||
{
|
||||
"id": "LRLFh0lVw2OIPJdW",
|
||||
"meta": {
|
||||
"instanceId": "",
|
||||
"templateCredsSetupCompleted": true
|
||||
},
|
||||
"name": "📈 Hourly Monitoring of Crypto Rates with Alpha Vantage API and Google Sheets",
|
||||
"tags": [],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "95d9375b-c423-4346-8dd4-3cba09a62caa",
|
||||
"name": "BTC Exchange Rate",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
-520,
|
||||
-60
|
||||
],
|
||||
"parameters": {
|
||||
"url": "https://www.alphavantage.co/query",
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpQueryAuth",
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "function",
|
||||
"value": "CURRENCY_EXCHANGE_RATE"
|
||||
},
|
||||
{
|
||||
"name": "from_currency",
|
||||
"value": "BTC"
|
||||
},
|
||||
{
|
||||
"name": "to_currency",
|
||||
"value": "EUR"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"httpQueryAuth": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "5d2a06c6-d2d8-4f55-b9ea-615d5c88612a",
|
||||
"name": "Call Every Hour",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"position": [
|
||||
-860,
|
||||
40
|
||||
],
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [
|
||||
{
|
||||
"field": "hours"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "ede798c5-aec6-4040-9ed8-b9f2cf6ea66f",
|
||||
"name": "Save Rate BTC",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
-320,
|
||||
-60
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"Ask_Price": "={{ $json['Realtime Currency Exchange Rate']['9. Ask Price'] }}",
|
||||
"Bid_Price": "={{ $json['Realtime Currency Exchange Rate']['8. Bid Price'] }}",
|
||||
"Time_Zone": "={{ $json['Realtime Currency Exchange Rate']['7. Time Zone'] }}",
|
||||
"Exchange_Rate": "={{ $json['Realtime Currency Exchange Rate']['5. Exchange Rate'] }}",
|
||||
"Last_Refreshed": "={{ $json['Realtime Currency Exchange Rate']['6. Last Refreshed'] }}",
|
||||
"To_Currency_Code": "={{ $json['Realtime Currency Exchange Rate']['3. To_Currency Code'] }}",
|
||||
"To_Currency_Name": "={{ $json['Realtime Currency Exchange Rate']['4. To_Currency Name'] }}",
|
||||
"From_Currency_Code": "={{ $json['Realtime Currency Exchange Rate']['1. From_Currency Code'] }}",
|
||||
"From_Currency_Name": "={{ $json['Realtime Currency Exchange Rate']['2. From_Currency Name'] }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "Last_Refreshed",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Last_Refreshed",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Time_Zone",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Time_Zone",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "From_Currency_Code",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "From_Currency_Code",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "From_Currency_Name",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "From_Currency_Name",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "To_Currency_Code",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "To_Currency_Code",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "To_Currency_Name",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "To_Currency_Name",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Exchange_Rate",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Exchange_Rate",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Last_Refreshed",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Last_Refreshed",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Time_Zone",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Time_Zone",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Bid_Price",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Bid_Price",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Ask_Price",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Ask_Price",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "BTC"
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "1vtjhgdulJn8niwZE0RQeQL8L1kb00L4z2TOuQK_9mV0",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "AlphaVantage"
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.6
|
||||
},
|
||||
{
|
||||
"id": "2fb8320b-aeb4-4e23-91c5-ff2ceebc8970",
|
||||
"name": "Save Rate ETH",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
-320,
|
||||
120
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"Ask_Price": "={{ $json['Realtime Currency Exchange Rate']['9. Ask Price'] }}",
|
||||
"Bid_Price": "={{ $json['Realtime Currency Exchange Rate']['8. Bid Price'] }}",
|
||||
"Time_Zone": "={{ $json['Realtime Currency Exchange Rate']['7. Time Zone'] }}",
|
||||
"Exchange_Rate": "={{ $json['Realtime Currency Exchange Rate']['5. Exchange Rate'] }}",
|
||||
"Last_Refreshed": "={{ $json['Realtime Currency Exchange Rate']['6. Last Refreshed'] }}",
|
||||
"To_Currency_Code": "={{ $json['Realtime Currency Exchange Rate']['3. To_Currency Code'] }}",
|
||||
"To_Currency_Name": "={{ $json['Realtime Currency Exchange Rate']['4. To_Currency Name'] }}",
|
||||
"From_Currency_Code": "={{ $json['Realtime Currency Exchange Rate']['1. From_Currency Code'] }}",
|
||||
"From_Currency_Name": "={{ $json['Realtime Currency Exchange Rate']['2. From_Currency Name'] }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "Last_Refreshed",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Last_Refreshed",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Time_Zone",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Time_Zone",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "From_Currency_Code",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "From_Currency_Code",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "From_Currency_Name",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "From_Currency_Name",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "To_Currency_Code",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "To_Currency_Code",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "To_Currency_Name",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "To_Currency_Name",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Exchange_Rate",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Exchange_Rate",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Last_Refreshed",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Last_Refreshed",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Time_Zone",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Time_Zone",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Bid_Price",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Bid_Price",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "Ask_Price",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "Ask_Price",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": 1591416661,
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "ETH"
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "1vtjhgdulJn8niwZE0RQeQL8L1kb00L4z2TOuQK_9mV0",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "AlphaVantage"
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.6
|
||||
},
|
||||
{
|
||||
"id": "b0630581-e5cc-4e64-bbb3-2c362b766a8a",
|
||||
"name": "ETH Exchange Rate",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
-520,
|
||||
120
|
||||
],
|
||||
"parameters": {
|
||||
"url": "https://www.alphavantage.co/query",
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"authentication": "genericCredentialType",
|
||||
"genericAuthType": "httpQueryAuth",
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "function",
|
||||
"value": "CURRENCY_EXCHANGE_RATE"
|
||||
},
|
||||
{
|
||||
"name": "from_currency",
|
||||
"value": "ETH"
|
||||
},
|
||||
{
|
||||
"name": "to_currency",
|
||||
"value": "EUR"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"httpQueryAuth": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "329e3c49-9b8f-49ca-83da-5e7ec00f17f0",
|
||||
"name": "Notification BTC",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"position": [
|
||||
-80,
|
||||
-60
|
||||
],
|
||||
"webhookId": "5f997b2e-aff9-427e-a92d-a0133903a3a4",
|
||||
"parameters": {
|
||||
"text": "=<b>🔄 BTC to EUR Rate Update</b>\n\n<b>From:</b> {{ $json[\"From_Currency_Name\"] }} ({{ $json[\"From_Currency_Code\"] }})\n<b>To:</b> {{ $json[\"To_Currency_Name\"] }} ({{ $json[\"To_Currency_Code\"] }})\n\n<b>💱 Exchange Rate:</b> {{ $json[\"Exchange_Rate\"] }}\n<b>📉 Bid:</b> {{ $json[\"Bid_Price\"] }}\n<b>📈 Ask:</b> {{ $json[\"Ask_Price\"] }}\n\n<b>🕒 Last Updated:</b> {{ $json[\"Last_Refreshed\"] }} ({{ $json[\"Time_Zone\"] }})",
|
||||
"chatId": "1698247520",
|
||||
"additionalFields": {
|
||||
"parse_mode": "HTML",
|
||||
"appendAttribution": false
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"telegramApi": {
|
||||
"id": "BlqWlzuNU8B4t9Pw",
|
||||
"name": "Assistant Bot"
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "e9920828-9a99-4899-9313-037fc78933dc",
|
||||
"name": "Notification ETH",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"position": [
|
||||
-80,
|
||||
120
|
||||
],
|
||||
"webhookId": "5f997b2e-aff9-427e-a92d-a0133903a3a4",
|
||||
"parameters": {
|
||||
"text": "=<b>🔄 ETH to EUR Rate Update</b>\n\n<b>From:</b> {{ $json[\"From_Currency_Name\"] }} ({{ $json[\"From_Currency_Code\"] }})\n<b>To:</b> {{ $json[\"To_Currency_Name\"] }} ({{ $json[\"To_Currency_Code\"] }})\n\n<b>💱 Exchange Rate:</b> {{ $json[\"Exchange_Rate\"] }}\n<b>📉 Bid:</b> {{ $json[\"Bid_Price\"] }}\n<b>📈 Ask:</b> {{ $json[\"Ask_Price\"] }}\n\n<b>🕒 Last Updated:</b> {{ $json[\"Last_Refreshed\"] }} ({{ $json[\"Time_Zone\"] }})",
|
||||
"chatId": "1698247520",
|
||||
"additionalFields": {
|
||||
"parse_mode": "HTML",
|
||||
"appendAttribution": false
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"telegramApi": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "3d674add-ca49-4e6f-b3e4-7aaa82f67953",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-900,
|
||||
-500
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 220,
|
||||
"height": 760,
|
||||
"content": "### 1. Workflow Trigger every hour\n\nTrigger the collection of ETH and BTC price every hour.\n\n#### How to setup?\n*Nothing to do.*"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "4095c6c9-9cbd-446c-884c-ed713760ddff",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-660,
|
||||
-500
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 920,
|
||||
"height": 760,
|
||||
"content": "### 2. Collect BTC and ETH price from Alpha Vantage Insight API\nThis starts by calling the CURRENCY_EXCHANGE_RATE function of the **Alpha Vantage Insight API** to get the exchange rate to euros.\n#### How to setup?\n- **Set up API credentials**\n 1. Get your free API key from [Alpha Vantage](https://www.alphavantage.co/support/#api-key)\n 2. Fill in the API key in the HTTP Request node using query parameters authentication\n- **Load records in the Google Sheet Node**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which you want to record your working sessions\n 4. Map the fields: `From_Currency_Code`, `From_Currency_Name`, `To_Currency_Code`, `To_Currency_Name`, `Exchange_Rate`, `Bid_Price`, `Ask_Price`, `Last_Refreshed`, `Time_Zone`\t\t\t\t\t\t\t\t\t\t\t\t\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n- **Send Telegram notification**\n 1. Add your Telegram credentials\n 2. Fill in the Chat ID and message content\n 3. Use dynamic variables for formatting the exchange rate alert\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"active": false,
|
||||
"pinData": {},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "cd5a0fe2-298c-40a5-b184-be90d8c220d1",
|
||||
"connections": {
|
||||
"Save Rate BTC": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Notification BTC",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Save Rate ETH": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Notification ETH",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Call Every Hour": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "BTC Exchange Rate",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "ETH Exchange Rate",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"BTC Exchange Rate": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Save Rate BTC",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"ETH Exchange Rate": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Save Rate ETH",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
*Tags: Supply Chain Management, Logistics, Transportation, Data Transmission*
|
||||
|
||||
### Context
|
||||
Hey! I'm Samir, a **Supply Chain Engineer and Data Scientist from Paris** founder of
|
||||
[LogiGreen Consulting](https://www.logi-green.com/)
|
||||
|
||||
We help **small and medium businesses** **improve their logistics processes** using AI, Data Analytics and Automation.
|
||||
|
||||
> Sustainable and Efficient supply chains with N8N!
|
||||
|
||||
📬 For business inquiries, you can add me on [Here](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### What is an EDI Message?
|
||||
Electronic Data Interchange (EDI) is **a standardized method** of automatically transferring data between computer systems.
|
||||
|
||||

|
||||
|
||||
They ensure the **smooth flow of essential transactional data**, such as **purchase orders**, invoices, **shipping notices**, and more.
|
||||
|
||||

|
||||
|
||||
For instance, a manufacturing company can receive purchase orders from a retailer via EDI.
|
||||
|
||||

|
||||
|
||||
However, they need **complex integration** for the transmission and processing of the messages.
|
||||
|
||||
### Who is this template for?
|
||||
This workflow template is designed for **small companies** that cannot connect to their customers and need to **manually process** the EDI messages received.
|
||||
|
||||
### How does it work?
|
||||
This workflow uses a **Gmail Trigger** that analyzes all the incoming emails.
|
||||
1. **📧 Gmail Trigger** → Detects emails with "EDI" in the subject.
|
||||
2. **📜 Parses EDI Message** → Uses a JavaScript Code Node to extract structured data.
|
||||
3. **📊 Formats the Data** → Converts it into a table-friendly format.
|
||||
4. **📑 Updates Google Sheets** → Automatically logs the processed orders.
|
||||
|
||||
### Prerequisite
|
||||
This workflow does not require any additional paying subscription.
|
||||
|
||||
- A **Google Drive Account** with a folder including a Google Sheet
|
||||
- API Credentials: **Google Drive API**, **Google Sheets API** and **Gmail API**
|
||||
- A Google sheet to store the shipment records. *You do not need to prepare the columns.*
|
||||
|
||||
### Next Steps
|
||||
Follow the **sticky notes** to set up the parameters inside each node and get ready to improve your logistics operations!
|
||||
|
||||
|
||||
**📺 Watch the Step-by-Step Guide**
|
||||
|
||||
[](https://youtu.be/-phwXeYk7Es)
|
||||
|
||||
🎥 [Check My Tutorial](https://youtu.be/-phwXeYk7Es)
|
||||
|
||||
🚀 Interested in applications of N8N for Logistics & Supply Chain Management? Let's connect on [Linkedin](www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Notes
|
||||
- This template includes an example of EDI message to test the workflow.
|
||||
- If you want to learn more about Electronic Data Interchange: 🚚 [Blog Article about Electronic Data Interchange (EDI)](https://www.samirsaci.com/what-is-edi-electronic-data-interchange/)
|
||||
|
||||
*This workflow has been created with N8N 1.82.1*
|
||||
*Submitted: March 19th, 2025*
|
||||
+1514
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,55 @@
|
||||
*Tags: EU News, RSS, AI Classifier, Data Table, Email Digest, Automation, n8n*
|
||||
|
||||
### Context
|
||||
|
||||
Hi! I’m Samir! A Supply Chain Engineer and Data Scientist based in Paris, and founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
This workflow is part of my project to **automatically monitor European Union press releases, filter them with AI, and send a curated email digest every morning**.
|
||||
|
||||

|
||||
|
||||
> Turn raw EU press releases into a clean, topic-based newsletter — without writing a single line of backend code.
|
||||
|
||||
By default, the workflow filters **sustainability-related news**, but you can easily adapt the topic description (e.g. AI, trade, digital, energy) in the AI node.
|
||||
|
||||
📬 For business inquiries, you can find me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This template is designed for:
|
||||
|
||||
- **Policy analysts and researchers** who want to track EU updates on a specific topic
|
||||
- **Consultants and sustainability teams** who need a daily view of relevant announcements
|
||||
|
||||
|
||||
### What does this workflow do?
|
||||
|
||||
This workflow acts as an **AI-powered EU news filter and digest generator**.
|
||||
|
||||
|
||||
1. **Fetches the latest press releases** from the Council of the EU RSS feed every morning at 09:00
|
||||
2. **Uses an AI classifier** (OpenAI) to decide whether each article is relevant to your topic
|
||||
3. **Stores only the relevant items** in an **n8n Data Table**
|
||||
4. **Generates a formatted HTML newsletter** grouping the day’s relevant articles
|
||||
5. **Sends the digest by email** using the Gmail node
|
||||
|
||||
Here’s an example of the generated email:
|
||||
|
||||

|
||||
|
||||
### 🎥 Tutorial
|
||||
|
||||
A complete tutorial (with explanations of every node) is available on YouTube:
|
||||
|
||||
[](https://youtu.be/vNavNGRqcK4)
|
||||
|
||||
### Next Steps
|
||||
|
||||
🗒️ Inside the workflow:
|
||||
|
||||
- Replace the Data Table reference with your own
|
||||
- Update the recipient email in the Gmail node with your credentials
|
||||
- Customize the HTML digest (colors, logo, style) in the Code node
|
||||
- Adjust the schedule time if necessary
|
||||
|
||||
*Submitted: 13 November 2025*
|
||||
@@ -0,0 +1,440 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": ""
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "f35387a3-bf8f-47ba-a29b-f9ec9e0dcecd",
|
||||
"name": "AI Classifier",
|
||||
"type": "@n8n/n8n-nodes-langchain.openAi",
|
||||
"position": [
|
||||
752,
|
||||
272
|
||||
],
|
||||
"parameters": {
|
||||
"modelId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gpt-4o-mini",
|
||||
"cachedResultName": "GPT-4O-MINI"
|
||||
},
|
||||
"options": {
|
||||
"temperature": 0.3
|
||||
},
|
||||
"messages": {
|
||||
"values": [
|
||||
{
|
||||
"content": "=You are an AI classifier for EU news. Your task is to determine if a news item is related to a specific topic.\n\nTopic Name: SUSTAINABILITY\nTopic Description: News related to climate change, carbon neutrality, Green Deal initiatives, renewable energy, energy efficiency, circular economy, biodiversity protection, sustainable finance, ESG regulations, environmental standards, emission reduction targets, clean technology, sustainable transport, green bonds, nature restoration, water management, waste reduction, sustainable agriculture, forest protection, ocean conservation, green transition funding, climate adaptation, net-zero policies, sustainable development goals (SDGs), environmental compliance, and eco-innovation in the European Union.\n\nNews Item to Classify:\nTitle: {{ $json.title }}\nContent: {{ $json.content }}\nCategories: {{ $json.categories.join(', ') }}\n\nAnalyze this news item and respond with a JSON object:\n{\n \"is_relevant\": true/false,\n \"relevance_score\": 0-100,\n \"key_topics_found\": [\"list\", \"of\", \"relevant\", \"aspects\"],\n \"reasoning\": \"Brief explanation of why this is or isn't relevant\"\n}\n\nOnly respond with the JSON object, no additional text."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"openAiApi": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"typeVersion": 1.4
|
||||
},
|
||||
{
|
||||
"id": "d0d172a5-ebb6-43fe-a77f-cdbd3d846e5c",
|
||||
"name": "Loop Over Items1",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"position": [
|
||||
512,
|
||||
272
|
||||
],
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "a3e6d386-e19d-47ef-9636-e99660551616",
|
||||
"name": "Trigger: 09:00 am",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"position": [
|
||||
16,
|
||||
272
|
||||
],
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [
|
||||
{
|
||||
"triggerAtHour": 9
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "aa2fbd6a-6eca-49a5-97c1-2daa91833c96",
|
||||
"name": "RSS News EU",
|
||||
"type": "n8n-nodes-base.rssFeedRead",
|
||||
"position": [
|
||||
256,
|
||||
272
|
||||
],
|
||||
"parameters": {
|
||||
"url": "https://www.consilium.europa.eu/en/rss/pressreleases.ashx",
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "994fbfd6-a5a6-43b6-92e2-02348a98ea4d",
|
||||
"name": "Insert row",
|
||||
"type": "n8n-nodes-base.dataTable",
|
||||
"position": [
|
||||
1312,
|
||||
272
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"guid": "={{ $('Loop Over Items1').item.json.guid }}",
|
||||
"link": "={{ $('Loop Over Items1').item.json.link }}",
|
||||
"title": "={{ $('Loop Over Items1').item.json.title }}",
|
||||
"content": "={{ $('Loop Over Items1').item.json.content }}",
|
||||
"categories": "={{ $('Loop Over Items1').item.json.categories.join(', ') }}",
|
||||
"createDate": "={{ $now.toFormat('yyyy-MM-dd') }}",
|
||||
"contentSnippet": "={{ $('Loop Over Items1').item.json.contentSnippet }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "title",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"readOnly": false,
|
||||
"required": false,
|
||||
"displayName": "title",
|
||||
"defaultMatch": false
|
||||
},
|
||||
{
|
||||
"id": "link",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"readOnly": false,
|
||||
"required": false,
|
||||
"displayName": "link",
|
||||
"defaultMatch": false
|
||||
},
|
||||
{
|
||||
"id": "content",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"readOnly": false,
|
||||
"required": false,
|
||||
"displayName": "content",
|
||||
"defaultMatch": false
|
||||
},
|
||||
{
|
||||
"id": "contentSnippet",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"readOnly": false,
|
||||
"required": false,
|
||||
"displayName": "contentSnippet",
|
||||
"defaultMatch": false
|
||||
},
|
||||
{
|
||||
"id": "guid",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"readOnly": false,
|
||||
"required": false,
|
||||
"displayName": "guid",
|
||||
"defaultMatch": false
|
||||
},
|
||||
{
|
||||
"id": "categories",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"readOnly": false,
|
||||
"required": false,
|
||||
"displayName": "categories",
|
||||
"defaultMatch": false
|
||||
},
|
||||
{
|
||||
"id": "createDate",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"readOnly": false,
|
||||
"required": false,
|
||||
"displayName": "createDate",
|
||||
"defaultMatch": false
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"dataTableId": {
|
||||
"__rl": true,
|
||||
"mode": "",
|
||||
"value": "",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
}
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "7583a248-5a3b-4a75-9129-1ea57f3255c3",
|
||||
"name": "If",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"position": [
|
||||
1104,
|
||||
272
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 2,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "0b998795-1a1f-4f17-8651-57b6fedc7be1",
|
||||
"operator": {
|
||||
"type": "boolean",
|
||||
"operation": "equals"
|
||||
},
|
||||
"leftValue": "={{ $json.message.content.parseJson().is_relevant }}",
|
||||
"rightValue": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 2.2
|
||||
},
|
||||
{
|
||||
"id": "1247ddc1-33b0-481d-9307-b7ef4849feb4",
|
||||
"name": "Collect Today's News",
|
||||
"type": "n8n-nodes-base.dataTable",
|
||||
"position": [
|
||||
304,
|
||||
784
|
||||
],
|
||||
"parameters": {
|
||||
"filters": {
|
||||
"conditions": [
|
||||
{
|
||||
"keyName": "createDate",
|
||||
"keyValue": "={{ $now.toFormat('yyyy-MM-dd') }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"matchType": "allConditions",
|
||||
"operation": "get",
|
||||
"dataTableId": {
|
||||
"__rl": true,
|
||||
"mode": "",
|
||||
"value": "",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
}
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "",
|
||||
"name": "Send the digest",
|
||||
"type": "n8n-nodes-base.gmail",
|
||||
"position": [
|
||||
848,
|
||||
784
|
||||
],
|
||||
"webhookId": "",
|
||||
"parameters": {
|
||||
"message": "={{ $json.html }}",
|
||||
"options": {
|
||||
"appendAttribution": false
|
||||
},
|
||||
"subject": "={{ $json.subject }}"
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 2.1
|
||||
},
|
||||
{
|
||||
"id": "235f4c77-1bfc-4549-bc72-9051c13f8716",
|
||||
"name": "Generate the Digest in HTML",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
544,
|
||||
784
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "// Get all news items from the input\nconst newsItems = $input.all().map(item => item.json);\n\n// Generate the HTML email\nconst htmlEmail = `\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>EU Sustainability News Digest</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;\n line-height: 1.6;\n color: #333;\n max-width: 700px;\n margin: 0 auto;\n padding: 0;\n background-color: #f5f5f5;\n }\n .container {\n background-color: #ffffff;\n margin: 20px auto;\n border-radius: 12px;\n overflow: hidden;\n box-shadow: 0 2px 10px rgba(0,0,0,0.1);\n }\n .header {\n background: linear-gradient(135deg, #2e7d32 0%, #66bb6a 100%);\n color: white;\n padding: 30px 40px;\n text-align: center;\n }\n .header h1 {\n margin: 0;\n font-size: 28px;\n font-weight: 600;\n letter-spacing: -0.5px;\n }\n .header p {\n margin: 10px 0 0 0;\n font-size: 16px;\n opacity: 0.95;\n }\n .date-badge {\n display: inline-block;\n background-color: rgba(255, 255, 255, 0.2);\n padding: 5px 15px;\n border-radius: 20px;\n margin-top: 10px;\n font-size: 14px;\n }\n .content {\n padding: 30px 40px;\n }\n .news-item {\n margin-bottom: 30px;\n padding-bottom: 30px;\n border-bottom: 1px solid #e0e0e0;\n }\n .news-item:last-of-type {\n border-bottom: none;\n margin-bottom: 0;\n padding-bottom: 0;\n }\n .news-number {\n display: inline-block;\n background-color: #2e7d32;\n color: white;\n width: 28px;\n height: 28px;\n border-radius: 50%;\n text-align: center;\n line-height: 28px;\n font-size: 14px;\n font-weight: 600;\n margin-right: 10px;\n flex-shrink: 0;\n }\n .news-title {\n font-size: 18px;\n font-weight: 600;\n color: #1a1a1a;\n margin: 0 0 10px 0;\n line-height: 1.4;\n display: flex;\n align-items: flex-start;\n }\n .news-title a {\n color: #1a1a1a;\n text-decoration: none;\n flex-grow: 1;\n }\n .news-title a:hover {\n color: #2e7d32;\n }\n .news-meta {\n display: flex;\n gap: 15px;\n margin-bottom: 12px;\n flex-wrap: wrap;\n }\n .category-tag {\n background-color: #e8f5e9;\n color: #2e7d32;\n padding: 4px 10px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 500;\n }\n .date-tag {\n color: #666;\n font-size: 12px;\n padding: 4px 0;\n }\n .news-content {\n color: #555;\n font-size: 15px;\n line-height: 1.6;\n margin: 12px 0 0 38px;\n }\n .read-more {\n display: inline-block;\n margin: 10px 0 0 38px;\n color: #2e7d32;\n text-decoration: none;\n font-size: 14px;\n font-weight: 500;\n }\n .read-more:hover {\n text-decoration: underline;\n }\n .footer {\n background-color: #f8f9fa;\n padding: 25px 40px;\n text-align: center;\n border-top: 1px solid #e0e0e0;\n }\n .footer-logo {\n font-size: 20px;\n font-weight: 600;\n color: #2e7d32;\n margin-bottom: 10px;\n }\n .footer-links {\n margin-top: 15px;\n }\n .footer-links a {\n color: #2e7d32;\n text-decoration: none;\n margin: 0 10px;\n font-size: 14px;\n }\n .footer-links a:hover {\n text-decoration: underline;\n }\n .footer-divider {\n margin: 0 5px;\n color: #999;\n }\n .footer-note {\n margin-top: 15px;\n font-size: 12px;\n color: #666;\n }\n @media (max-width: 600px) {\n .header {\n padding: 25px 20px;\n }\n .header h1 {\n font-size: 24px;\n }\n .content {\n padding: 20px;\n }\n .footer {\n pLine truncated
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "a11e0d02-2932-495d-8e59-0addb6dd1bd7",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 412,
|
||||
"height": 492,
|
||||
"content": "### 1. Fetch the latest EU news from RSS\nUsing the RSS feed of the Council of the European Union, the workflow runs every morning at 09:00 and pulls the most recent press releases.\nThe RSS node turns each news item (title, link, content and categories) into structured items that can be processed one by one in the next steps.\n\n**How to setup?**\n*Nothing to do*\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "490f9974-78f7-4f89-9eaa-ddae094600a3",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
448,
|
||||
0
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1068,
|
||||
"height": 492,
|
||||
"content": "### 2. Use AI to select relevant news and store them\nEach news item is looped through the AI Classifier, the model returns a JSON object with is_relevant, a relevance score and key topics. Only items marked as relevant are kept and inserted into an n8n Data Table.\n\n**How to setup?**\n- Add your Open AI Credentials and select the model\n- Setup your datatable and select it in the node\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "3ecdb292-dcca-45b2-9c34-4e0bafcd8596",
|
||||
"name": "Sticky Note3",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
272,
|
||||
512
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1040,
|
||||
"height": 436,
|
||||
"content": "### 3. Build the daily digest and send it by email \nAt the end of the day’s run, the workflow retrieves all news stored for today from the Data Table.\nA Code node formats them into a clean HTML newsletter (headline, tags, short description and link for each article).\nFinally, the Gmail node sends the digest to your chosen inbox.\n\n#### How to setup?\nUpdate the recipient address in the Gmail node and adjust the HTML template or branding in the Code node if needed.\n- Select the right table\n- Add your Gmail credentials\n [Learn more about the Gmail Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.gmail)"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"If": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Insert row",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Insert row": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"RSS News EU": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"AI Classifier": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "If",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Loop Over Items1": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "AI Classifier",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Trigger: 09:00 am": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "RSS News EU",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Collect Today's News",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Collect Today's News": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Generate the Digest in HTML",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Generate the Digest in HTML": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send the digest",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
*Tags: Marketing, Image Processing, Automation*
|
||||
|
||||
### Context
|
||||
|
||||
Hey! I’m Samir, a Data Scientist from Paris and the founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
We use **AI, automation, and data** to support sustainable business practices for small, medium and large companies.
|
||||
|
||||
I implemented this workflow to support an event agency to automate **image processing like background removal** using Photoroom API.
|
||||
|
||||

|
||||
|
||||
> Automate your photos processing with n8n!
|
||||
|
||||
This n8n workflow collects all images in a **Google Drive** folder shared with multiple photographers.
|
||||
|
||||
For each image, it calls the **Photoroom API**:
|
||||
- A processed image w/o a background is saved in a subfolder **Remove Background**
|
||||
- The original pictures are saved in the subfolder **Original**
|
||||
|
||||
This workflow, triggered every morning, will **process the backlog of images**.
|
||||
|
||||

|
||||
|
||||
📬 For business inquiries, feel free to connect with me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow is useful for:
|
||||
- **Digital Marketing** teams that use images for content creation
|
||||
- **Photographs** or **Event Organisers** that collect large amounts of photos that need processing
|
||||
|
||||
|
||||
### What does it do?
|
||||
|
||||
This n8n workflow:
|
||||
1. ⏰ Triggers automatically every morning
|
||||
2. 🖼️ Collects the names and IDs of all images in the folder
|
||||
3. 🧹 HTTP POST request to Photoroom API to remove the background
|
||||
4. 📄 Stores the processed image and the original image in two separate sub-folders
|
||||
|
||||

|
||||
|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
You’ll need:
|
||||
- A **Google Drive Account** connected to your n8n instance with credentials
|
||||
- A **Photoroom API** key that you can get for free (trial) here: [Photoroom API](https://docs.photoroom.com/)
|
||||
|
||||
### Follow the Guide!
|
||||
|
||||
Follow the **sticky notes** inside the workflow or check out my step-by-step tutorial on how to configure and deploy it.
|
||||
|
||||
[](https://www.youtube.com/watch?v=gBn9wd0fJ54)
|
||||
|
||||
🎥 [Watch My Tutorial](https://www.youtube.com/watch?v=gBn9wd0fJ54)
|
||||
|
||||
|
||||
|
||||
*This workflow was built using n8n version 1.93.0*
|
||||
*Submitted: May 26, 2025*
|
||||
+373
@@ -0,0 +1,373 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": "",
|
||||
"templateCredsSetupCompleted": true
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "b2441a46-bd10-4c0c-a804-3be138d2fd8e",
|
||||
"name": "Loop Over Items",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"position": [
|
||||
-460,
|
||||
220
|
||||
],
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "c98d1021-81f1-43e9-8001-8708a16c5fdc",
|
||||
"name": "Loop Over Items1",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"position": [
|
||||
-440,
|
||||
860
|
||||
],
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "d6b4b139-1751-4e53-8bd3-e86d0b427301",
|
||||
"name": "Move to Original",
|
||||
"type": "n8n-nodes-base.googleDrive",
|
||||
"position": [
|
||||
260,
|
||||
880
|
||||
],
|
||||
"parameters": {
|
||||
"fileId": {
|
||||
"__rl": true,
|
||||
"mode": "id",
|
||||
"value": "={{ $json.id }}"
|
||||
},
|
||||
"driveId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "My Drive"
|
||||
},
|
||||
"folderId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Original"
|
||||
},
|
||||
"operation": "move"
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "0be7ea89-f48b-4844-ad2a-29b7e23e0fd2",
|
||||
"name": "Save to Remove Background",
|
||||
"type": "n8n-nodes-base.googleDrive",
|
||||
"position": [
|
||||
220,
|
||||
300
|
||||
],
|
||||
"parameters": {
|
||||
"name": "={{ $json.name }}",
|
||||
"driveId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "My Drive"
|
||||
},
|
||||
"options": {},
|
||||
"folderId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Remove Background"
|
||||
},
|
||||
"inputDataFieldName": "result-sandbox-mode.png"
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "73369386-e3c9-452d-a89f-6bf66072d002",
|
||||
"name": "Download Image",
|
||||
"type": "n8n-nodes-base.googleDrive",
|
||||
"position": [
|
||||
-220,
|
||||
300
|
||||
],
|
||||
"parameters": {
|
||||
"fileId": {
|
||||
"__rl": true,
|
||||
"mode": "id",
|
||||
"value": "={{ $json.id }}"
|
||||
},
|
||||
"options": {},
|
||||
"operation": "download"
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "b127c813-9ae3-48d2-85da-36d2db1c6e63",
|
||||
"name": "Get all files",
|
||||
"type": "n8n-nodes-base.googleDrive",
|
||||
"position": [
|
||||
-800,
|
||||
220
|
||||
],
|
||||
"parameters": {
|
||||
"filter": {
|
||||
"folderId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "1j1wl00y4RAsRRecU74XxR6SpncYxMqdA",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
},
|
||||
"whatToSearch": "files"
|
||||
},
|
||||
"options": {},
|
||||
"resource": "fileFolder"
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "c3546db3-2a28-4f98-87bd-1556c8ee3e8c",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-1260,
|
||||
-120
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 600,
|
||||
"height": 560,
|
||||
"content": "### 1. Collect all original images file names\nTrigger the workflow to collect the names of all the images you want to process that are stored in a specific folder.\n\n### How to set up?\n- **Search all files of a specific Google Drive Folder**:\n 1. Add your Google Drive API credentials to access the Google Drive Folders\n 2. Select the folder that contains all the files you want to process\n[Learn more about the Google Drive API](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googledrive/)\n- **Prepare the subfolders**:\n - Create a sub-folder to store processed images named: **Remove Background**\n - Create a sub-folder to store the original files after processing name: **Original**\n \n\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "18181d47-99e1-4ec6-9f0c-4e46c5f42315",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-520,
|
||||
-120
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1000,
|
||||
"height": 620,
|
||||
"content": "### 2. Remove backgrounds with Photoroom API & save the results in the sub-folder **Remove Background**\nFor each image, you use the API to remove the background. The result is saved in the sub-folder **Remove Background**.\n\n### How to set up?\n- **Download file from Google Drive**:\n 1. Add your Google Drive API credentials to access the Google Drive Folders\n 2. Select the sub-folder location\n[Learn more about the Google Drive API](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googledrive/)\n- **Remove Background with Photoroom API**:\n 1. Add your Photoroom API key \n[Learn more about the Photoroom API](https://docs.photoroom.com/)\n\n*You can test the API with the free sandbox mode that enables 1,000 calls per month.*\n\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "91ffb1ae-3a55-460b-8d29-6f28ae33c83d",
|
||||
"name": "Photoroom API Remove Background",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
0,
|
||||
300
|
||||
],
|
||||
"parameters": {
|
||||
"url": "https://image-api.photoroom.com/v2/edit",
|
||||
"method": "POST",
|
||||
"options": {
|
||||
"response": {
|
||||
"response": {
|
||||
"responseFormat": "file",
|
||||
"outputPropertyName": "result-sandbox-mode.png"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendBody": true,
|
||||
"contentType": "multipart-form-data",
|
||||
"sendHeaders": true,
|
||||
"bodyParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "shadow.mode",
|
||||
"value": "ai.soft"
|
||||
},
|
||||
{
|
||||
"name": "background.color",
|
||||
"value": "FFFFFF"
|
||||
},
|
||||
{
|
||||
"name": "padding",
|
||||
"value": "0.1"
|
||||
},
|
||||
{
|
||||
"name": "imageFile",
|
||||
"parameterType": "formBinaryData",
|
||||
"inputDataFieldName": "data"
|
||||
}
|
||||
]
|
||||
},
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "image/png, application/json"
|
||||
},
|
||||
{
|
||||
"name": "x-api-key"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "5c99a353-1a07-400e-996a-fbd83c735dce",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-520,
|
||||
540
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1000,
|
||||
"height": 560,
|
||||
"content": "### 3. Move Processed Images \nAfter processing, the images are moved to the sub-folder **Original**.\n\n### How to set up?\n- **Move file in Google Drive**:\n 1. Add your Google Drive API credentials to access the Google Drive Folders\n 2. Select the sub-folder \n[Learn more about the Google Drive API](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googledrive/)\n- **Remove Background with Photoroom API**:\n 1. Add your Photoroom API key \n[Learn more about the Photoroom API](https://docs.photoroom.com/)\n\n*You can test the API with the free sandbox mode that enables 1,000 calls per month.*\n\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "3a7e0bcd-eb5a-4609-9359-8e632cace6d3",
|
||||
"name": "Daily at 08:00 am",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"position": [
|
||||
-1020,
|
||||
220
|
||||
],
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [
|
||||
{
|
||||
"triggerAtHour": 8
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "8281445b-9cf1-4e43-8a19-9e3b26b8d67e",
|
||||
"name": "Sticky Note3",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
500,
|
||||
-120
|
||||
],
|
||||
"parameters": {
|
||||
"width": 660,
|
||||
"height": 460,
|
||||
"content": "### [📺Complete Tutorial](https://www.youtube.com/watch?v=gBn9wd0fJ54)\n\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"Get all files": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Loop Over Items1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Download Image": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Photoroom API Remove Background",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Loop Over Items": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Download Image",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Loop Over Items1": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Move to Original",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Move to Original": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Daily at 08:00 am": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get all files",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Save to Remove Background": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Photoroom API Remove Background": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Save to Remove Background",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
*Tags: Sustainability, Web Scraping, OpenAI, Google Sheets, Newsletter, Marketing*
|
||||
|
||||
### Context
|
||||
|
||||
Hey! I’m Samir, a Supply Chain Engineer and Data Scientist from Paris, and the founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
We use **AI, automation, and data** to support sustainable business practices for small, medium and large companies.
|
||||
|
||||
I use this workflow to bring **awareness about sustainability** and **promote my business** by delivering automated daily news digests.
|
||||
|
||||

|
||||
|
||||
> Promote your business with a fully automated newsletter powered by AI!
|
||||
|
||||
This n8n workflow scrapes articles from the official EU news website and sends a **daily curated digest**, highlighting only the **most relevant sustainability news**.
|
||||
|
||||
📬 For business inquiries, feel free to connect with me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow is useful for:
|
||||
- **Business owners** who want to promote their service or products with a fully automated newsletter
|
||||
- **Sustainability professionals** staying informed on EU climate news
|
||||
- **Consultants and analysts** working on CSRD, Green Deal, or ESG initiatives
|
||||
- **Corporate communications teams** tracking relevant EU activity
|
||||
- **Media curators** building newsletters
|
||||
|
||||
### What does it do?
|
||||
|
||||
This n8n workflow:
|
||||
1. ⏰ Triggers automatically every morning
|
||||
2. 🌍 Scrapes articles from the EU Commission News Portal
|
||||
3. 🧠 Uses **OpenAI GPT-4o** to classify each article for sustainability relevance
|
||||
4. 📄 Stores the results in a **Google Sheet** for tracking
|
||||
5. 🧾 Generates a **beautiful HTML digest email**, including titles, summaries, and images
|
||||
6. 📬 Sends the digest via **Gmail** to your mailing list
|
||||
|
||||
### How it works
|
||||
|
||||

|
||||
|
||||
1. **Trigger** at 08:30 every morning
|
||||
2. **Scrape and extract** article blocks from the EU news site
|
||||
3. **Use OpenAI** to decide if articles are sustainability-related
|
||||
4. **Store relevant entries** in Google Sheets
|
||||
5. **Generate HTML email** with a professional layout and logo
|
||||
6. **Send the digest** via Gmail to a configured recipient list
|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
You’ll need:
|
||||
- A **Google Sheet** connected to your n8n instance
|
||||
- An **OpenAI account** with GPT-4 or GPT-4o access
|
||||
- A **Gmail OAuth credential** setup
|
||||
|
||||
### Follow the Guide!
|
||||
|
||||
Follow the **sticky notes** inside the workflow or check out my step-by-step tutorial on how to configure and deploy it.
|
||||
|
||||
[](https://www.youtube.com/watch?v=q8VCAUbuat8)
|
||||
|
||||
🎥 [Watch My Tutorial](https://www.youtube.com/watch?v=q8VCAUbuat8)
|
||||
|
||||
### Notes
|
||||
|
||||
- You can customize the system prompt to adjust how AI classifies “sustainability”
|
||||
- Works well for tracking updates relevant to **climate action**, **green transition**, and **circular economy**
|
||||
|
||||
*This workflow was built using n8n version 1.85.4*
|
||||
*Submitted: April 24, 2025*
|
||||
+734
@@ -0,0 +1,734 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": "=",
|
||||
"templateCredsSetupCompleted": true
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "10d2d97d-428e-4224-beae-e4ce4e090e4f",
|
||||
"name": "If",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"position": [
|
||||
3220,
|
||||
2500
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 2,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "7f8ac804-088d-4dfa-a661-8b6b09a6e340",
|
||||
"operator": {
|
||||
"name": "filter.operator.equals",
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
},
|
||||
"leftValue": "={{ $json.date }}",
|
||||
"rightValue": "={{ $now.minus(5,\"day\").day }} {{ $now.minus(5,\"day\").monthLong }} {{ $now.minus(5,\"day\").year }}"
|
||||
},
|
||||
{
|
||||
"id": "094bd21e-1d23-4f06-a286-501045a53c9b",
|
||||
"operator": {
|
||||
"name": "filter.operator.equals",
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
},
|
||||
"leftValue": "={{ $json.type }}",
|
||||
"rightValue": "News article"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 2.2
|
||||
},
|
||||
{
|
||||
"id": "968fac7c-48be-4fe1-a1d0-3c1fd828b0bc",
|
||||
"name": "OpenAI Chat Model3",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"position": [
|
||||
3640,
|
||||
2480
|
||||
],
|
||||
"parameters": {
|
||||
"model": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gpt-4o-mini"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "fde7d8e1-4124-4506-abb7-8e400ad2729b",
|
||||
"name": "Agent Classification",
|
||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||
"position": [
|
||||
3660,
|
||||
2300
|
||||
],
|
||||
"parameters": {
|
||||
"text": "=Title: {{$json.title}}\nDescription: {{$json.description}}\n\nIs this article about sustainability? Return only: true or false\n",
|
||||
"options": {
|
||||
"systemMessage": "=You are a classification assistant. \n\nYour role is to analyze the title and description of an article and determine if it is related to sustainability. \n\nYou must only return {\"answer\": true} if the article is clearly related to sustainability (e.g., environmental protection, renewable energy, sustainable development, climate action, green economy, etc.). \n\nIf it is not clearly related, return {\"answer\": false}.\n\nIf the description is empty or missing, rely only on the title. Your response must be only one of the two JSON options: {\"answer\": true} or {\"answer\": false}. Do not provide explanations.\n"
|
||||
},
|
||||
"promptType": "define",
|
||||
"hasOutputParser": true
|
||||
},
|
||||
"typeVersion": 1.8
|
||||
},
|
||||
{
|
||||
"id": "670c0877-008f-4943-9a6b-c5e543ae6482",
|
||||
"name": "Get Sustainability News",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
2380,
|
||||
2880
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"filtersUI": {
|
||||
"values": [
|
||||
{
|
||||
"lookupValue": "true",
|
||||
"lookupColumn": "sustainability"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "=",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.5
|
||||
},
|
||||
{
|
||||
"id": "ba6605af-5b5f-44d1-b47d-4246c2b999f3",
|
||||
"name": "Send to your mailing list",
|
||||
"type": "n8n-nodes-base.gmail",
|
||||
"position": [
|
||||
2740,
|
||||
2880
|
||||
],
|
||||
"webhookId": "=",
|
||||
"parameters": {
|
||||
"sendTo": "email@gmail.com",
|
||||
"message": "={{ $json.email_body }}",
|
||||
"options": {
|
||||
"appendAttribution": false
|
||||
},
|
||||
"subject": "Your Sustainability News Digest from LogiGreen"
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 2.1
|
||||
},
|
||||
{
|
||||
"id": "5d662a41-969a-49d8-a594-f0f962d51350",
|
||||
"name": "Generate Email HTML",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
2560,
|
||||
2880
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "const summary = `Welcome to the EU Sustainability News Digest provided by <a href=\"https://logi-green.com\" style=\"color: #0077cc; text-decoration: none;\">LogiGreen Consulting</a>.`;\n\nconst articles = items.map(item => item.json); // each item is an article\n\nlet html = `\n<div style=\"font-family: Arial, sans-serif; max-width: 700px; margin: auto;\">\n <h2 style=\"color: #2c3e50;\">🌍 EU News Digest – ${new Date().toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' })}</h2>\n <p style=\"font-size: 16px; color: #333;\">${summary}</p>\n <hr style=\"border: 1px solid #eee;\" />\n`;\n\nfor (const article of articles) {\n const link = article.link.startsWith(\"http\") ? article.link : `https://ec.europa.eu${article.link}`;\n html += `\n <div style=\"display: flex; margin: 20px 0; border-bottom: 1px solid #ddd; padding-bottom: 15px;\">\n ${article.image ? `<img src=\"${article.image}\" alt=\"\" width=\"150\" style=\"margin-right: 15px; border-radius: 6px; object-fit: cover;\" />` : ''}\n <div>\n <p style=\"margin: 0; font-size: 12px; color: #888;\">${article.type} | ${article.date}</p>\n <h3 style=\"margin: 5px 0;\">\n <a href=\"${link}\" style=\"text-decoration: none; color: #0077cc;\">${article.title}</a>\n </h3>\n <p style=\"margin: 5px 0; color: #333;\">${article.description || ''}</p>\n ${article.read_time ? `<p style=\"font-size: 12px; color: gray;\">${article.read_time}</p>` : ''}\n </div>\n </div>\n `;\n}\n\nhtml += `\n <div style=\"margin-top: 40px; padding-top: 20px; border-top: 1px solid #eee; text-align: center;\">\n <p style=\"font-size: 12px; color: #999;\">You received this email as part of the EU Sustainability News Digest project.</p>\n <a href=\"https://logi-green.com\" target=\"_blank\">\n <img src=\"https://www.logi-green.com/web/image/website/1/logo/LogiGreen%20Consulting?unique=e2af3c6\" alt=\"LogiGreen Consulting Logo\" style=\"height: 40px; margin-top: 10px;\" />\n </a>\n </div>\n</div>\n`;\n\n\nreturn [{ json: { email_body: html } }];\n"
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "378789d8-7b01-40ca-8bd5-96e1d137445d",
|
||||
"name": "Parse Article Blocks",
|
||||
"type": "n8n-nodes-base.html",
|
||||
"position": [
|
||||
3000,
|
||||
2500
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"operation": "extractHtmlContent",
|
||||
"dataPropertyName": "articles",
|
||||
"extractionValues": {
|
||||
"values": [
|
||||
{
|
||||
"key": "type",
|
||||
"cssSelector": "ul.ecl-content-block__primary-meta-container li:nth-child(1)"
|
||||
},
|
||||
{
|
||||
"key": "date",
|
||||
"cssSelector": "ul.ecl-content-block__primary-meta-container li:nth-child(2) time\t"
|
||||
},
|
||||
{
|
||||
"key": "title",
|
||||
"cssSelector": "div.ecl-content-block__title a\t"
|
||||
},
|
||||
{
|
||||
"key": "link",
|
||||
"attribute": "href",
|
||||
"cssSelector": "div.ecl-content-block__title a\t",
|
||||
"returnValue": "attribute"
|
||||
},
|
||||
{
|
||||
"key": "description",
|
||||
"cssSelector": "div.ecl-content-block__description p\t"
|
||||
},
|
||||
{
|
||||
"key": "image",
|
||||
"attribute": "src",
|
||||
"cssSelector": "picture img",
|
||||
"returnValue": "attribute"
|
||||
},
|
||||
{
|
||||
"key": "read_time",
|
||||
"cssSelector": "ul.ecl-content-block__secondary-meta-container span.ecl-content-block__secondary-meta-label\t"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "cf7017a4-b996-452b-8aca-6f37964bd288",
|
||||
"name": "Extract Articles Blocks",
|
||||
"type": "n8n-nodes-base.html",
|
||||
"position": [
|
||||
2560,
|
||||
2500
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"operation": "extractHtmlContent",
|
||||
"extractionValues": {
|
||||
"values": [
|
||||
{
|
||||
"key": "articles",
|
||||
"cssSelector": "div.ecl-content-item-block__item",
|
||||
"returnArray": true,
|
||||
"returnValue": "html"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "d6f27e99-d866-4c27-9e99-5c579f505751",
|
||||
"name": "Trigger at 08:30 am",
|
||||
"type": "n8n-nodes-base.scheduleTrigger",
|
||||
"position": [
|
||||
2120,
|
||||
2500
|
||||
],
|
||||
"parameters": {
|
||||
"rule": {
|
||||
"interval": [
|
||||
{
|
||||
"triggerAtHour": 8,
|
||||
"triggerAtMinute": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "1cc2483b-72ca-415d-90fc-a9b3ed0f6de8",
|
||||
"name": "Query EU News Website",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
2340,
|
||||
2500
|
||||
],
|
||||
"parameters": {
|
||||
"url": "https://commission.europa.eu/news-and-media/news_en",
|
||||
"options": {}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "93bb792d-7979-4b68-a026-df960ea3cd8d",
|
||||
"name": "Split Out by Article Block",
|
||||
"type": "n8n-nodes-base.splitOut",
|
||||
"position": [
|
||||
2780,
|
||||
2500
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"fieldToSplitOut": "articles"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "127662c6-5561-4d35-9ca5-d23b26c223e9",
|
||||
"name": "Loop Over Articles",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"position": [
|
||||
3440,
|
||||
2500
|
||||
],
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "900bb98e-5b83-4e29-81f8-2f04478f9c2e",
|
||||
"name": "Sustainability Flag",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"position": [
|
||||
4040,
|
||||
2300
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "dcfc8260-1125-4883-8895-8a5f55d09341",
|
||||
"name": "sustainability",
|
||||
"type": "string",
|
||||
"value": "={{ $json.output.answer }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"retryOnFail": false,
|
||||
"typeVersion": 3.4
|
||||
},
|
||||
{
|
||||
"id": "a6114158-8842-4cb5-b43b-0c4cb3134e0e",
|
||||
"name": "Merge Article + Flag",
|
||||
"type": "n8n-nodes-base.merge",
|
||||
"position": [
|
||||
4260,
|
||||
2360
|
||||
],
|
||||
"parameters": {
|
||||
"mode": "combineBySql"
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 3.1
|
||||
},
|
||||
{
|
||||
"id": "585e6348-9af4-49e8-b30b-605d04921a88",
|
||||
"name": "Record Results",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
4480,
|
||||
2440
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"date": "={{ $json.date }}",
|
||||
"link": "={{ $json.link }}",
|
||||
"type": "={{ $json.type }}",
|
||||
"image": "={{ $json.image }}",
|
||||
"title": "={{ $json.title }}",
|
||||
"read_time": "={{ $json.read_time }}",
|
||||
"description": "={{ $json.description }}",
|
||||
"sustainability": "={{ $json.sustainability }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "sustainability",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "sustainability",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "type",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "type",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "date",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "date",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "title",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "title",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "link",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "link",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "description",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "description",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "image",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "image",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "read_time",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "read_time",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "=",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.5
|
||||
},
|
||||
{
|
||||
"id": "78743430-d367-45b9-8d79-72dfdd436e3b",
|
||||
"name": "Structured Output Parser",
|
||||
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
|
||||
"position": [
|
||||
3920,
|
||||
2480
|
||||
],
|
||||
"parameters": {
|
||||
"jsonSchemaExample": "{\n \"answer\": \"boolean | null\"\n}\n"
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "a5bc414c-3a8c-45f2-ae73-9dbe591a9bae",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
2060,
|
||||
1960
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 220,
|
||||
"height": 680,
|
||||
"content": "### 1. Workflow Trigger with Cron Job\nThe workflow is triggered every morning at 08:30 am (local time)\n\n#### How to setup?\n- Select the time you want to set it up\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "a21f729a-2e9d-4c7d-a31c-e68c54ee613e",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
2300,
|
||||
2680
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 620,
|
||||
"height": 380,
|
||||
"content": "### 4. Generate HTML page and send by email\nThis block collects all the articles of the day to create a prettified HTML page that is sent using the Gmail node.\n#### How to setup?\n- **Gmail Node:** set up your Gmail API credentials\n[Learn more about the Gmail Trigger Node]\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "5b79acce-0b33-493d-ba90-a93fa6f32fbb",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
2300,
|
||||
1960
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 840,
|
||||
"height": 700,
|
||||
"content": "### 2. Scrapping and Parsing of Articles blocks\nThis starts with the HTTP node collecting HTML code that is parsed to extract Article Titles, Link, Image Cover and Reading time.\n\n#### How to setup?\n*Nothing to do*"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "5705f302-1c6e-4a99-a653-f093186787f5",
|
||||
"name": "Sticky Note3",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
3220,
|
||||
1960
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1440,
|
||||
"height": 700,
|
||||
"content": "### 3. Classifiy all the articles (Sustainability: true or false)\nThis starts with the If node that filters based on the scope date fixed by you. Through the loop, the AI Agent classify the articles using the title and description.\nThe ones that are flagged as \"sustainability\" are recorded in a Google Sheet.\n\n#### How to setup?\n- **Record results in the Google Sheet Node**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which you want to record the articles\n 4. Map the fields: **sustainability, type, date, title, link, description, image, read time**\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n- **AI Agent with the Chat Model**:\n 1. Add a chat model with the required credentials *(Example: Open AI 4o-mini)*"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "f7a0f75d-c70e-46cb-a260-6c05d890e63c",
|
||||
"name": "Sticky Note4",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
2960,
|
||||
2680
|
||||
],
|
||||
"parameters": {
|
||||
"width": 580,
|
||||
"height": 380,
|
||||
"content": "### [Check the Tutorial](https://www.youtube.com/watch?v=q8VCAUbuat8)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"If": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Articles",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Record Results": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Articles",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Loop Over Articles": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Agent Classification",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Merge Article + Flag",
|
||||
"type": "main",
|
||||
"index": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"OpenAI Chat Model3": {
|
||||
"ai_languageModel": [
|
||||
[
|
||||
{
|
||||
"node": "Agent Classification",
|
||||
"type": "ai_languageModel",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Generate Email HTML": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Send to your mailing list",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Sustainability Flag": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Merge Article + Flag",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Trigger at 08:30 am": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Query EU News Website",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Get Sustainability News",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Agent Classification": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Sustainability Flag",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Merge Article + Flag": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Record Results",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Parse Article Blocks": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "If",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Query EU News Website": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Articles Blocks",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Articles Blocks": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Split Out by Article Block",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Sustainability News": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Generate Email HTML",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Structured Output Parser": {
|
||||
"ai_outputParser": [
|
||||
[
|
||||
{
|
||||
"node": "Agent Classification",
|
||||
"type": "ai_outputParser",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Split Out by Article Block": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Parse Article Blocks",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
*Tags: Supply Chain, Logistics, Control Tower*
|
||||
|
||||
### Context
|
||||
|
||||
Hey! I’m Samir, a Supply Chain Engineer and Data Scientist from Paris, and the founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
We design tools to help companies improve their **logistics processes** using data analytics, AI, and automation—to **reduce costs and minimize environmental impact**.
|
||||
|
||||
> Let’s use N8N to build smarter and more sustainable supply chains!
|
||||
|
||||
📬 For business inquiries, you can add me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow template is designed for **logistics operations** that need a monitoring solution for their **distribution chains**.
|
||||
|
||||

|
||||
|
||||
Connected to your Transportation Management Systems, this AI agent can answer any question about the shipments handled by your distribution teams.
|
||||
|
||||
### How does it work?
|
||||
|
||||
The workflow is connected to a **Google BigQuery** table that stores **outbound order data** (customer deliveries).
|
||||
|
||||

|
||||
|
||||
Here’s what the AI agent does:
|
||||
1. 🤔 Receives a user question via chat.
|
||||
2. 🧠 Understands the request and **generates the correct SQL query**.
|
||||
3. ✅ Executes the SQL query using a **BigQuery** node.
|
||||
4. 💬 Responds to the user in **plain English**.
|
||||
|
||||

|
||||
|
||||
Thanks to the chat memory, users can ask follow-up questions to dive deeper into the data.
|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
This workflow requires **no advanced programming skills**.
|
||||
|
||||
You’ll need:
|
||||
- A **Google BigQuery account** with an SQL table storing transactional records.
|
||||
- An **OpenAI API key** (GPT-4o) for the chat model.
|
||||
|
||||
### Next Steps
|
||||
|
||||
Follow the sticky notes in the workflow to configure each node and start using **AI to support your supply chain operations**.
|
||||
|
||||
[](https://www.loom.com/share/50271f9d50214d7184830985497a75ec?sid=d0c410dc-29f1-488f-b89a-4011de0ded07)
|
||||
|
||||
[🎥 Watch My Tutorial](https://www.loom.com/share/50271f9d50214d7184830985497a75ec?sid=d0c410dc-29f1-488f-b89a-4011de0ded07)
|
||||
|
||||
🚀 Curious how N8N can transform your logistics operations?
|
||||
|
||||
### Notes
|
||||
|
||||
- The chat trigger can easily be replaced with **Teams**, **Telegram**, or **Slack** for a better user experience.
|
||||
- You can also connect this to a customer chat window using a **webhook**.
|
||||
|
||||
*This workflow was built using N8N version 1.82.1*
|
||||
*Submitted: March 24, 2025*
|
||||
@@ -0,0 +1,293 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": "6a5e68bcca67c4cdb3e0b698d01739aea084e1ec06e551db64aeff43d174cb23"
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "53b36910-966f-45ba-a425-a3260a55059f",
|
||||
"name": "OpenAI Chat Model",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"position": [
|
||||
340,
|
||||
480
|
||||
],
|
||||
"parameters": {
|
||||
"model": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gpt-4o-mini"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "177235e8-c925-43d0-9695-10f072e26350",
|
||||
"name": "AI Control Tower Agent",
|
||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||
"position": [
|
||||
380,
|
||||
240
|
||||
],
|
||||
"parameters": {
|
||||
"options": {
|
||||
"systemMessage": "=You are an AI-powered SQL assistant specialized in supply chain analytics. \nYour role is to execute SQL queries on BigQuery and return only the results in a structured format.\n\nToday we are May 31, 2021.\n\n### **Behavior & Rules**\n1️⃣ **Query Execution:**\n - Your only task is to process user requests and return **direct results** from BigQuery.\n - Do **not** display the SQL query.\n - Only return structured **data** as output.\n\n2️⃣ **Data Presentation:**\n - Format the results as a **table** whenever possible.\n - If results are numerical (counts, percentages, aggregates), return them **clearly and concisely**.\n - If results contain multiple rows, return **only the first 10** for preview, unless the user specifies otherwise.\n\n3️⃣ **Handling Large Datasets:**\n - If the user asks for many rows, show the first **100 rows max** unless specified.\n - Provide a **summary** when dealing with large data instead of showing everything.\n\n4️⃣ **Response Format:**\n - ✅ **For counts & metrics:** \n `\"There were 5,432 delayed shipments in the last 21 days.\"`\n - ✅ **For tables:** \n | ShipmentID | City | Store | Order Date | Delivery Date | On Time? |\n |-----------|-------|--------|------------|--------------|----------|\n | 12345 | NYC | ST1 | 2024-03-10 | 2024-03-15 | No |\n | 67890 | Paris | ST4 | 2024-03-11 | 2024-03-16 | Yes |\n\n5️⃣ **Clarifying Unclear Requests:**\n - If the user request is **too broad**, ask for clarification instead of running an expensive query.\n\n---\n\n### Schema Awareness\nAll SQL queries must use the BigQuery table: \n`transport.shipments` \n\nThis table includes fields such as:\n- `Shipment ID`, `City`, `Store`, `Order Date`, `Delivery Date`, `On Time Delivery`\n- As well as operational timestamps: `Transmission`, `Loading`, `Airport Arrival`, etc.\n- And status flags: `Transmission OnTime`, `Loading OnTime`, `Airport OnTime`, `Store Open`\n\nUse these fields appropriately when analyzing shipment performance.\n\n---\n\n### Tool Usage Instruction (for \"bigquery_tool\")\n\nWhenever you need to run a SQL query, use the tool called `bigquery_tool`.\n\nYou must provide the query in the following format:\n```json\n{\n \"query\": \"SELECT COUNT(*) FROM `transport.shipments` WHERE `On Time Delivery` = FALSE\"\n}\n"
|
||||
}
|
||||
},
|
||||
"typeVersion": 1.8
|
||||
},
|
||||
{
|
||||
"id": "5366cc5f-85d3-44d2-9b1b-62febfcb44e3",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-100,
|
||||
-120
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 200,
|
||||
"height": 520,
|
||||
"content": "### 1. Workflow Trigger with Chat\nThis workflow uses a simple chat window as a trigger. You can replace it with Telegram, Slack, Teams or a webhook trigger linked to your chat.\n\n#### How to setup?\n*Nothing to do.*\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "4218a062-12f8-437d-ab22-5a653a3089b2",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
140,
|
||||
-120
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 700,
|
||||
"height": 740,
|
||||
"content": "### 2. AI Agent equipped with the query tool\nIn order to have more control on the input of the BigQuery node, we don't use the BigQuery tool. Instead we have a set of nodes to retrieve the SQL query, clean it and send it to a BigQuery Node.\n\n#### How to setup?\n- **AI Agent with the Chat Model**:\n 1. Add a **chat model** with the required credentials *(Example: Open AI 4o-mini)*\n 2. Adapt the **name of your BigQuery table** in the system prompt *(Example: transports.shipments)*\n 3. Adapt the **tables fields explanation** in the system prompt\n [Learn more about the AI Agent Node](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent)\n- Copy and past the **nodes in the yellow sticker** in another workflow. Point the query tool to this workflow.\n[Learn more about the Custom n8n Workflow Tool node](https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolworkflow)"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "c5967f58-00e8-4f03-9110-913547f7ab9c",
|
||||
"name": "Call Query Tool",
|
||||
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
|
||||
"position": [
|
||||
640,
|
||||
440
|
||||
],
|
||||
"parameters": {
|
||||
"name": "bigquery_tool",
|
||||
"workflowId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "4Os7DoxHjFuTwWio",
|
||||
"cachedResultName": "🔨 Big Query Tool"
|
||||
},
|
||||
"description": "=Use this tool to run an SQL query and fetch the result from the BigQuery database.\n\nThe tool expects input in the following format:\n{\n \"query\": \"SELECT COUNT(*) FROM `transport.shipments` WHERE `On Time Delivery` = FALSE\"\n}\n\nOnly provide the SQL query as a string inside the 'query' key. Do not include code formatting (like ```sql), comments, or explanations. The tool will return only the raw result from the database.\n",
|
||||
"workflowInputs": {
|
||||
"value": {
|
||||
"query": "={{ $fromAI(\"query\", \"SQL query to run\") }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "query",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "query",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [
|
||||
"query"
|
||||
],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
}
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "429813c8-b07f-4551-aeea-1744a1225449",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
900,
|
||||
-120
|
||||
],
|
||||
"parameters": {
|
||||
"width": 760,
|
||||
"height": 460,
|
||||
"content": "### 3. Big Query Workflow\nExecute the SQL query generated by the AI agent in Big Query. Retrieve the results and send them back to the AI Agent.\n\n### How to set up?\n- Paste these nodes in a separate workflow so you can use it with multiple agents.\n- **Google BigQuery API**:\n 1. Add your Google Translate API credentials\n 2. The project in which your table is located\n [Learn more about the Google BigQuery Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlebigquery)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "bede0624-8923-4af0-8adc-8be22d556066",
|
||||
"name": "Query Database",
|
||||
"type": "n8n-nodes-base.googleBigQuery",
|
||||
"position": [
|
||||
1520,
|
||||
180
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"sqlQuery": "={{ $json.query }}",
|
||||
"projectId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "=",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 2.1
|
||||
},
|
||||
{
|
||||
"id": "137e4dbc-db8d-4ec7-a3e0-478dde6ef27c",
|
||||
"name": "Trigger Executed by the AI Tool",
|
||||
"type": "n8n-nodes-base.executeWorkflowTrigger",
|
||||
"position": [
|
||||
960,
|
||||
180
|
||||
],
|
||||
"parameters": {
|
||||
"workflowInputs": {
|
||||
"values": [
|
||||
{
|
||||
"name": "query"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 1.1
|
||||
},
|
||||
{
|
||||
"id": "42a2801e-582e-4340-83af-ef0041eab4f9",
|
||||
"name": "Sanitising the Query",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
1240,
|
||||
180
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "return [\n {\n json: {\n query: $input.first().json.query.replace(/```sql|```/g, \"\").trim()\n }\n }\n];\n"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "7c86fda0-116c-47ad-aaf5-8b83d2c083c6",
|
||||
"name": "Chat Memory",
|
||||
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
|
||||
"position": [
|
||||
480,
|
||||
480
|
||||
],
|
||||
"parameters": {},
|
||||
"typeVersion": 1.3
|
||||
},
|
||||
{
|
||||
"id": "e1408ac1-24da-4d38-8fdf-c110a54d3f55",
|
||||
"name": "Chat with the User",
|
||||
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
|
||||
"position": [
|
||||
-60,
|
||||
240
|
||||
],
|
||||
"webhookId": "ee7c418b-d7d6-41f9-8e87-0f71b8ae1cf9",
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 1.1
|
||||
},
|
||||
{
|
||||
"id": "bc49829b-45f2-4910-9c37-907271982f14",
|
||||
"name": "Sticky Note3",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
900,
|
||||
380
|
||||
],
|
||||
"parameters": {
|
||||
"width": 780,
|
||||
"height": 540,
|
||||
"content": "### 4. Do you need more details?\nFind a step-by-step guide in this tutorial\n\n[🎥 Watch My Tutorial](https://www.loom.com/share/50271f9d50214d7184830985497a75ec?sid=d0c410dc-29f1-488f-b89a-4011de0ded07)"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"Chat Memory": {
|
||||
"ai_memory": [
|
||||
[
|
||||
{
|
||||
"node": "AI Control Tower Agent",
|
||||
"type": "ai_memory",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Call Query Tool": {
|
||||
"ai_tool": [
|
||||
[
|
||||
{
|
||||
"node": "AI Control Tower Agent",
|
||||
"type": "ai_tool",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"OpenAI Chat Model": {
|
||||
"ai_languageModel": [
|
||||
[
|
||||
{
|
||||
"node": "AI Control Tower Agent",
|
||||
"type": "ai_languageModel",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Chat with the User": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "AI Control Tower Agent",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Sanitising the Query": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Query Database",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Trigger Executed by the AI Tool": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Sanitising the Query",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
_Tags: Supply Chain Management, Logistics, Transportation_
|
||||
|
||||
### Context
|
||||
|
||||
Hey! I'm Samir, a **Supply Chain Engineer and Data Scientist from Paris** founder of
|
||||
[LogiGreen Consulting](https://www.logi-green.com/)
|
||||
|
||||
We design tools to help **small and medium businesses** in **improving their logistics processes** using data analytics and **automation**.
|
||||
|
||||
> Let's use N8N to make supply chains **more efficient and sustainable Supply Chains!**
|
||||
|
||||
📬 For business inquiries, you can add me on [Here](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow template is designed for **logistics operations** that cannot rely on a **Transportation Management System** to **record proofs of deliveries**.
|
||||
|
||||

|
||||
|
||||
### What is a delivery confirmation?
|
||||
|
||||
This workflow uses a **Telegram bot** to automatically notify **logistics teams** by email when a **shipment is delivered**.
|
||||
|
||||

|
||||
|
||||
Drivers (equipped with their smartphones) can **record their arrival** with all the necessary information for accurate distribution planning.
|
||||
|
||||
### How do we notify the delivery?
|
||||
|
||||
Let us imagine a truck driver arriving at the destination; he can contact the bot to be guided on how to record the delivery.
|
||||
|
||||

|
||||
|
||||
0. **User Guide**: the first bot's message is a brief explanation of the process
|
||||
1. **Record Shipment Number**: the bot asks the driver to share the shipment number and record it
|
||||
2. **Collect GPS Location**: the bot asks the driver to share its GPS location and record them
|
||||
3. **Picture of the Shipment**: the bot asks for a picture of the shipment and saves it in Google Drive
|
||||
4. **Send Confirmation**: after data collection, the bot proposes to send a confirmation to the logistics management team
|
||||
|
||||
An email is then automatically sent by the N8N workflow including all the information recorded by the flow and a picture of the shipment.
|
||||
|
||||

|
||||
|
||||
### Prerequisite
|
||||
|
||||
This workflow does not require any additional paying subscription.
|
||||
|
||||
- A **Google Drive Account** with a folder including a Google Sheet
|
||||
- API Credentials: **Google Drive API**, **Google Sheets API** and **Gmail API**
|
||||
- A Telegram Bot with its API token from BotFather
|
||||
- A Google sheet to store the shipment records with these five columns prepared: **shipmentNumber, recordTime, gpsLattitude, gpsLongitude, cargoPicture, deliveryTime**
|
||||
|
||||
### Next Steps
|
||||
|
||||
Follow the **sticky notes** to set up the parameters inside each node and get ready to improve your logistics operations!
|
||||
|
||||
I have detailed the steps in a **short tutorial** 👇
|
||||
[](https://youtu.be/9NS4RYaOwJ8)
|
||||
🎥 [Check My Tutorial](https://youtu.be/9NS4RYaOwJ8)
|
||||
|
||||
🚀 Interested in applications of N8N for Logistics & Supply Chain Management? Let's connect on [Linkedin](www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Notes
|
||||
|
||||
- This workflow can be adapted to add more functionalities. I explain how in the video.
|
||||
- The bot can handle multiple drivers at the same time.
|
||||
- If you want to learn more about the original tool designed with Python: 🚚 [Blog Article about Telegram Shipment Tracking Bot](https://www.samirsaci.com/build-a-shipment-tracking-tool-using-a-telegram-bot/)
|
||||
|
||||
_This workflow has been created with N8N 1.82.1_
|
||||
_Submitted: March 17th, 2025_
|
||||
+1217
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,73 @@
|
||||
***Tags**: Sustainability, Supply Chain, AI Agent, CO2 Emissions, Carbon Interface API, Logistics, Automation*
|
||||
|
||||
### Context
|
||||
|
||||
Hi! I’m Samir — a Supply Chain Engineer and Data Scientist based in Paris, and founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
I help logistics teams reduce their environmental footprint by combining **AI automation** and **carbon estimation APIs**.
|
||||
|
||||
This workflow is part of our **green logistics initiative**, allowing businesses to **track the CO₂ emissions** of last-mile or regional shipments.
|
||||
|
||||
> Automate carbon tracking for shipping operations with n8n!
|
||||
|
||||
📬 For business inquiries, feel free to connect with me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow is designed for **logistics coordinators**, **transportation planners**, or **sustainability officers** who want to estimate and record emissions for B2B shipments.
|
||||
|
||||
Let’s imagine your carrier sends a **shipment confirmation email** after a pickup is scheduled:
|
||||
|
||||

|
||||
|
||||
An **AI Agent** reads the email and extracts structured data: **addresses, distance, cargo weight, and delivery time**.
|
||||
|
||||
The **Carbon Interface API** is then called to calculate **CO₂ emissions based on weight and distance**, and the results are stored in a **Google Sheet**.
|
||||
|
||||

|
||||
|
||||
### How does it work?
|
||||
|
||||
This workflow automates the process of **tracking CO₂ emissions for scheduled shipments**:
|
||||
|
||||
- 📨 Gmail Trigger captures shipment confirmation emails
|
||||
- 🧠 AI Agent parses the shipment info (pickup, delivery, weight, distance)
|
||||
- 🚚 Carbon Interface API estimates CO₂ emissions
|
||||
- 📊 Google Sheets is used to store shipment metadata and carbon results
|
||||
|
||||
### Steps:
|
||||
|
||||
1. 💌 Trigger on new shipment confirmation email
|
||||
2. 🧠 Extract structured shipment info with AI Agent
|
||||
3. 📋 Store metadata in Google Sheets
|
||||
4. ⚙️ Call Carbon Interface API with weight and distance
|
||||
5. 📥 Append estimated CO₂ emissions to the shipment row
|
||||
|
||||

|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
You’ll need:
|
||||
|
||||
- A **Gmail account** to receive shipment confirmation emails
|
||||
- A **Google Sheet** to track shipment data and CO₂
|
||||
- A free [Carbon Interface API key](https://www.carboninterface.com/docs)
|
||||
- OpenAI access for using the AI Agent parser
|
||||
- A few sample emails from your logistics provider to test
|
||||
|
||||
### 📺 Learn More with n8n Tutorials
|
||||
|
||||
Get hands-on experience with automation through my comprehensive **n8n tutorial playlist**, featuring 18+ complete, step-by-step videos and ready-to-use templates.
|
||||
|
||||
[](https://bit.ly/playlist-n8n-supplyscience)
|
||||
|
||||
### Next Steps
|
||||
|
||||
🗒️ Use the sticky notes in the n8n canvas to:
|
||||
|
||||
- Add your Gmail and Carbon Interface credentials
|
||||
- Try with a sample shipment confirmation email
|
||||
- Check your Google Sheet to verify emissions and timestamps
|
||||
|
||||
*This template was built using n8n v1.93.0*
|
||||
*Submitted: June 7, 2025*
|
||||
+664
@@ -0,0 +1,664 @@
|
||||
{
|
||||
"id": "umOZqpde6cAUTL8F",
|
||||
"meta": {
|
||||
"instanceId": "",
|
||||
"templateCredsSetupCompleted": true
|
||||
},
|
||||
"name": "🚚 CO2 Emissions of Freight Shipments with Carbon Interface API and GPT-4o",
|
||||
"tags": [],
|
||||
"nodes": [
|
||||
{
|
||||
"id": "dc20a34f-90ff-4f4e-a2f7-7b77abb92f4c",
|
||||
"name": "Gmail Trigger",
|
||||
"type": "n8n-nodes-base.gmailTrigger",
|
||||
"position": [
|
||||
-320,
|
||||
415
|
||||
],
|
||||
"parameters": {
|
||||
"simple": false,
|
||||
"filters": {},
|
||||
"options": {
|
||||
"downloadAttachments": false
|
||||
},
|
||||
"pollTimes": {
|
||||
"item": [
|
||||
{
|
||||
"mode": "everyMinute"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"gmailOAuth2": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "984a0707-919c-4c67-b070-6b1caaea2946",
|
||||
"name": "Structured Output Parser",
|
||||
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
|
||||
"position": [
|
||||
140,
|
||||
600
|
||||
],
|
||||
"parameters": {
|
||||
"jsonSchemaExample": "{\n \"shipment_number\": \"7894-PCB-654\",\n \"pickup_location\": \"Centre de Distribution Auchan\",\n \"pickup_address\": \"77 Rue Albert Garry, 94450 Limeil-Brévannes\",\n \"expected_pickup_time\": \"2024-09-24T09:00:00\",\n \"temperature_control\": \"Frozen\",\n \"destination_store_name\": \"Auchan Store 124\",\n \"destination_address\": \"43-45 Quai Jules Guesde, 94405 Vitry-sur-Seine\",\n \"expected_delivery_time\": \"2024-09-24T18:00:00\",\n \"driving_distance_km\": 9.61,\n \"estimated_transit_min\": 16.9,\n \"cargo_quantity\": 12,\n \"cargo_weight_tons\": 2.79\n}\n"
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "de58b711-25ef-42f7-a5ab-4bc1d20fd1fc",
|
||||
"name": "AI Agent Parser",
|
||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||
"position": [
|
||||
-100,
|
||||
415
|
||||
],
|
||||
"parameters": {
|
||||
"text": "=Extract the structured shipment details from the following email:\n\n{{ $json.text }}\n",
|
||||
"options": {
|
||||
"systemMessage": "=You are a logistics assistant. When provided with the body of a shipment confirmation email, extract the following structured JSON object:\n\n{\n \"shipment_number\": string,\n \"pickup_location\": string,\n \"pickup_address\": string,\n \"expected_pickup_time\": string (ISO 8601, e.g. \"2024-09-24T09:00:00\"),\n \"temperature_control\": string,\n \"destination_store_name\": string,\n \"destination_address\": string,\n \"expected_delivery_time\": string (ISO 8601, e.g. \"2024-09-24T18:00:00\"),\n \"driving_distance_km\": number,\n \"estimated_transit_min\": number,\n \"cargo_quantity\": number,\n \"cargo_weight_tons\": number\n}\n\n- Extract **only** what’s listed.\n- Normalize dates and times to ISO 8601 format.\n- Remove units from numbers (e.g. 9.61 km → 9.61, 2.79 tons → 2.79).\n- Return only the JSON object. No additional explanation or text."
|
||||
},
|
||||
"promptType": "define",
|
||||
"hasOutputParser": true
|
||||
},
|
||||
"typeVersion": 1.9
|
||||
},
|
||||
{
|
||||
"id": "05ca643f-774c-40a5-843f-76790c42d433",
|
||||
"name": "OpenAI Chat Model2",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"position": [
|
||||
-100,
|
||||
620
|
||||
],
|
||||
"parameters": {
|
||||
"model": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gpt-4o-mini"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"credentials": {
|
||||
"openAiApi": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "fb16f384-e73f-4c3f-be72-24fede7b2760",
|
||||
"name": "Collect CO2 Emissions",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
680,
|
||||
420
|
||||
],
|
||||
"parameters": {
|
||||
"url": "https://www.carboninterface.com/api/v1/estimates",
|
||||
"method": "POST",
|
||||
"options": {},
|
||||
"jsonBody": "={\n \"type\": \"shipping\",\n \"weight_value\": {{ $('AI Agent Parser').item.json.output.cargo_weight_tons * 1000000 }},\n \"weight_unit\": \"g\",\n \"distance_value\": {{ $('AI Agent Parser').item.json.output.driving_distance_km }},\n \"distance_unit\": \"km\",\n \"transport_method\": \"truck\"\n}\n",
|
||||
"sendBody": true,
|
||||
"sendHeaders": true,
|
||||
"specifyBody": "json",
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer YOUR_TOKEN_HERE"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "82c2db80-9a3a-421b-ad27-097a713fc3c2",
|
||||
"name": "Load Results",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
920,
|
||||
420
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"co2_kg": "={{ $json.data.attributes.carbon_kg }}",
|
||||
"shipment_number": "={{ $('AI Agent Parser').item.json.output.shipment_number }}",
|
||||
"co2_estimation_time": "={{ $json.data.attributes.estimated_at }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "shipment_number",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "shipment_number",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "pickup_location",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "pickup_location",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "pickup_address",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "pickup_address",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "expected_pickup_time",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "expected_pickup_time",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "temperature_control",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "temperature_control",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "destination_store_name",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "destination_store_name",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "destination_address",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "destination_address",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "expected_delivery_time",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "expected_delivery_time",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "driving_distance_km",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "driving_distance_km",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "estimated_transit_min",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "estimated_transit_min",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "cargo_quantity",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "cargo_quantity",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "cargo_weight_tons",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"required": false,
|
||||
"displayName": "cargo_weight_tons",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "co2_kg",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "co2_kg",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "co2_estimation_time",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "co2_estimation_time",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [
|
||||
"shipment_number"
|
||||
],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "appendOrUpdate",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": 877993777,
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "CO2 emissions"
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "1NLNviRoVMEj2z78KiueruB5sd2CnvAb80DS7J2melVs",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Open Route Service"
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.6
|
||||
},
|
||||
{
|
||||
"id": "94cd225d-1bc0-42b5-a466-7fad8b445e94",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-360,
|
||||
-60
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 180,
|
||||
"height": 820,
|
||||
"content": "### 1. Workflow Trigger with Gmail Trigger\nThe workflow is triggered by a new email received in your Gmail mailbox dedicated to process **shipment records**.\n\n#### How to setup?\n- **Gmail Trigger Node:** set up your Gmail API credentials\n[Learn more about the Gmail Trigger Node](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.gmailtrigger)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "3e4989f9-2ef4-4c60-98cc-d8b988162e54",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-160,
|
||||
-60
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 440,
|
||||
"height": 820,
|
||||
"content": "### 2. AI Agent to trip schedule report\nThis node will analyze the content of an email to extract structured information about a **shipment**, including **pickup/delivery times**, **addresses**, **cargo weight**, and **driving distance**.\n\n#### How to setup?\n- **AI Agent with the Chat Model**:\n 1. Add a **chat model** with the required credentials *(Example: Open AI 4o-mini)*\n 2. Adapt the system prompt to the format of emails you expect to receive *(type of information included)*\n [Learn more about the AI Agent Node](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "7dc4dab6-2536-4373-b058-7487fc968c33",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
300,
|
||||
-60
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1180,
|
||||
"height": 820,
|
||||
"content": "### 3. Record Shipment Request Information and fetch emissions using the Carbon Interface API \nThis starts by recording all the structured shipment information parsed by the AI Agent node. Then, we use the **Carbon Interface API** to estimate **CO₂ emissions** based on cargo weight, distance, and transportation method.\n\n#### How to setup?\n\n- **Setup Carbon Interface API Credentials** \n 1. Get your free API key: [Carbon Interface API Documentation](https://docs.carboninterface.com/#/) \n 2. Fill the API key in the HTTP Request node headers (`Bearer YOUR_TOKEN_HERE`)\n\n- **Load records into the Google Sheet Node** \n 1. Add your Google Sheets API credentials to access your sheet \n 2. Select the file using the list, a URL, or its ID \n 3. Select the sheet where you want to log your shipping emissions \n 4. Map the fields: \n **shipment_number**, **pickup_location**, **pickup_address**, **expected_pickup_time**, **temperature_control**, **destination_store_name**, **destination_address**, **expected_delivery_time**, **driving_distance_km**, **estimated_transit_min**, **cargo_quantity**, **cargo_weight_tons**, **weight_value_g**, **co2_kg**, **co2_estimation_time**\n\n> Distance and weight values will be used to calculate carbon emissions via the Carbon Interface API.\n\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "a51645cd-d3fe-4c3f-a7af-9bdd31eb9242",
|
||||
"name": "Record Shipment Information",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
420,
|
||||
420
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"cargo_quantity": "={{ $json.output.cargo_quantity }}",
|
||||
"pickup_address": "={{ $json.output.pickup_address }}",
|
||||
"pickup_location": "={{ $json.output.pickup_location }}",
|
||||
"shipment_number": "={{ $json.output.shipment_number }}",
|
||||
"cargo_weight_tons": "={{ $json.output.cargo_weight_tons }}",
|
||||
"destination_address": "={{ $json.output.destination_address }}",
|
||||
"driving_distance_km": "={{ $json.output.driving_distance_km }}",
|
||||
"temperature_control": "={{ $json.output.temperature_control }}",
|
||||
"expected_pickup_time": "={{ $json.output.expected_pickup_time }}",
|
||||
"estimated_transit_min": "={{ $json.output.estimated_transit_min }}",
|
||||
"destination_store_name": "={{ $json.output.destination_store_name }}",
|
||||
"expected_delivery_time": "={{ $json.output.expected_delivery_time }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "shipment_number",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "shipment_number",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "pickup_location",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "pickup_location",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "pickup_address",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "pickup_address",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "expected_pickup_time",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "expected_pickup_time",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "temperature_control",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "temperature_control",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "destination_store_name",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "destination_store_name",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "destination_address",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "destination_address",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "expected_delivery_time",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "expected_delivery_time",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "driving_distance_km",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "driving_distance_km",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "estimated_transit_min",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "estimated_transit_min",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "cargo_quantity",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "cargo_quantity",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "cargo_weight_tons",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "cargo_weight_tons",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "co2_kg",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "co2_kg",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "co2_estimation_time",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "co2_estimation_time",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [
|
||||
"shipment_number"
|
||||
],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "appendOrUpdate",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": 877993777,
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "CO2 emissions"
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Open Route Service"
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"googleSheetsOAuth2Api": {
|
||||
"id": "",
|
||||
"name": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.6
|
||||
}
|
||||
],
|
||||
"active": false,
|
||||
"pinData": {
|
||||
"AI Agent Parser": [
|
||||
{
|
||||
"json": {
|
||||
"output": {
|
||||
"cargo_quantity": 12,
|
||||
"pickup_address": "77 Rue Albert Garry, 94450 Limeil-Brévannes",
|
||||
"pickup_location": "Centre de Distribution Auchan",
|
||||
"shipment_number": "7894-PCB-654",
|
||||
"cargo_weight_tons": 3.79,
|
||||
"destination_address": "43-45 Quai Jules Guesde, 94405 Vitry-sur-Seine",
|
||||
"driving_distance_km": 9.61,
|
||||
"temperature_control": "Frozen",
|
||||
"expected_pickup_time": "2024-09-24T09:00:00",
|
||||
"estimated_transit_min": 16.9,
|
||||
"destination_store_name": "Auchan Store 124",
|
||||
"expected_delivery_time": "2024-09-24T18:00:00"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "844e09c0-2706-43da-bd53-6c211b68aafc",
|
||||
"connections": {
|
||||
"Load Results": {
|
||||
"main": [
|
||||
[]
|
||||
]
|
||||
},
|
||||
"Gmail Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent Parser",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"AI Agent Parser": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Record Shipment Information",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"OpenAI Chat Model2": {
|
||||
"ai_languageModel": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent Parser",
|
||||
"type": "ai_languageModel",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Collect CO2 Emissions": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Load Results",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Structured Output Parser": {
|
||||
"ai_outputParser": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent Parser",
|
||||
"type": "ai_outputParser",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Record Shipment Information": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Collect CO2 Emissions",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
***Tags**: Supply Chain, Logistics, Route Planning, Transportation, GPS API*
|
||||
|
||||
### Context
|
||||
|
||||
Hi! I’m Samir — a Supply Chain Engineer and Data Scientist based in Paris, and founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
I help companies improve their **logistics operations** using data, AI, and automation to **reduce costs and minimize environmental footprint**.
|
||||
|
||||
> Let’s use n8n to build smarter and greener transport operations!
|
||||
|
||||
📬 For business inquiries, you can add find me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow is designed for **logistics and transport teams** who want to automate **distance and travel time calculations for truck shipments**.
|
||||
|
||||

|
||||
|
||||
Ideal for:
|
||||
- Control tower dashboards
|
||||
- Transport cost simulations
|
||||
- Route optimization studies
|
||||
|
||||

|
||||
|
||||
### How does it work?
|
||||
|
||||
This n8n workflow connects to a **Google Sheet** where you store city-to-city shipment lanes, and uses the **OpenRouteService API** to calculate:
|
||||
|
||||
- 📏 Distance (in meters)
|
||||
- ⏱️ Travel time (in seconds)
|
||||
- 🪪 Number of route steps
|
||||
|
||||
### Steps:
|
||||
1. ✅ Load departure/destination city coordinates from a Google Sheet
|
||||
2. 🔁 Loop through each record
|
||||
3. 🚚 Query OpenRouteService using the **truck (driving-hgv)** profile
|
||||
4. 🧾 Extract and store results: distance, duration, number of steps
|
||||
5. 📤 Update the Google Sheet with new values
|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
This workflow is beginner-friendly and requires:
|
||||
|
||||
- A **Google Sheet** with route pairs (departure and destination coordinates)
|
||||
- A free **OpenRouteService API key**
|
||||
[👉 Get one here](https://openrouteservice.org/dev/#/signup)
|
||||
|
||||
### Next Steps
|
||||
|
||||
🗒️ Follow the sticky notes inside the workflow to:
|
||||
|
||||
- Select your sheet
|
||||
- Plug in your API key
|
||||
- Launch the flow!
|
||||
|
||||
[](https://www.youtube.com/watch?v=uJtp2NsmR2E)
|
||||
|
||||
[🎥 Check the Tutorial](https://www.youtube.com/watch?v=uJtp2NsmR2E)
|
||||
|
||||
🚀 You can customize the workflow to:
|
||||
- Add **CO2 emission estimates** for Sustainability Reporting
|
||||
- Connect to your TMS via API or EDI
|
||||
|
||||
*This template was built using n8n v1.93.0*
|
||||
*Submitted: June 1, 2025*
|
||||
@@ -0,0 +1,437 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": ""
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "4660840a-499a-4d44-be7c-1d0edfee9634",
|
||||
"name": "Loop Over Items",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"position": [
|
||||
660,
|
||||
420
|
||||
],
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "4401f929-3ad6-4b8d-90bf-2edc58d4b381",
|
||||
"name": "Extract Results",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"position": [
|
||||
1220,
|
||||
440
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "4d0acd1a-c049-4353-9fb7-8eb08c138b9c",
|
||||
"name": "distance",
|
||||
"type": "string",
|
||||
"value": "={{ $json.features[0].properties.segments[0].distance }}"
|
||||
},
|
||||
{
|
||||
"id": "2a6ff46a-f620-4dad-a60a-018b8add76d5",
|
||||
"name": "duration",
|
||||
"type": "string",
|
||||
"value": "={{ $json.features[0].properties.segments[0].duration }}"
|
||||
},
|
||||
{
|
||||
"id": "de20932f-4eb5-4372-ad6d-64679df104cb",
|
||||
"name": "n_steps",
|
||||
"type": "number",
|
||||
"value": "={{ $json.features[0].properties.segments[0].steps.length }}"
|
||||
},
|
||||
{
|
||||
"id": "540df5ea-cf63-47cc-8d63-3d6fcac825af",
|
||||
"name": "",
|
||||
"type": "string",
|
||||
"value": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 3.4
|
||||
},
|
||||
{
|
||||
"id": "14165f3a-104a-415a-b84b-86e903f86be8",
|
||||
"name": "Request Open Route API",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
980,
|
||||
440
|
||||
],
|
||||
"parameters": {
|
||||
"url": "https://api.openrouteservice.org/v2/directions/driving-hgv",
|
||||
"options": {
|
||||
"response": {
|
||||
"response": {
|
||||
"responseFormat": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sendQuery": true,
|
||||
"sendHeaders": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "api_key"
|
||||
},
|
||||
{
|
||||
"name": "start",
|
||||
"value": "={{ $json.longitude_departure }}, {{ $json.latitude_departure }}"
|
||||
},
|
||||
{
|
||||
"name": "end",
|
||||
"value": "={{ $json.longitude_destination }}, {{ $json.latitude_destination }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json; charset=utf-8"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json, application/geo+json, application/gpx+xml, img/png; charset=utf-8"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "27f17e62-6528-4c29-b10e-38f79dc50811",
|
||||
"name": "Collect Routes",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
300,
|
||||
420
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Distance"
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "1NLNviRoVMEj2z78KiueruB5sd2CnvAb80DS7J2melVs",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": "Open Route Service"
|
||||
}
|
||||
},
|
||||
"typeVersion": 4.6
|
||||
},
|
||||
{
|
||||
"id": "0ceec3de-34c0-4361-b6fc-527d2108cd4d",
|
||||
"name": "Save Results",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
1440,
|
||||
440
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"id": "={{ $('Loop Over Items').item.json.id }}",
|
||||
"n_steps": "={{ $json.n_steps }}",
|
||||
"distance": "={{ $json.distance }}",
|
||||
"duration": "={{ $json.duration }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "id",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "id",
|
||||
"defaultMatch": true,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "city_departure",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "city_departure",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "longitude_departure",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "longitude_departure",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "latitude_departure",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "latitude_departure",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "city_destination",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "city_destination",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "longitude_destination",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "longitude_destination",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "latitude_destination",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "latitude_destination",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "distance",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "distance",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "duration",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "duration",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "n_steps",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "n_steps",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "row_number",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"readOnly": true,
|
||||
"required": false,
|
||||
"displayName": "row_number",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [
|
||||
"id"
|
||||
],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "update",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.6
|
||||
},
|
||||
{
|
||||
"id": "40e078a1-4cd5-46f0-938d-756c4a5d150d",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 540,
|
||||
"height": 640,
|
||||
"content": "### 1. Trigger the workflow\nThis starts by collecting all the routes (Departure, Destination) that need distance and driving time by **truck**.\n\n#### How to setup?\n\n- **Load records in the Google Sheet Node**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which you want to record your working sessions\n 4. Map the fields: **city_departure**, **longitude_departure** , **latitude_departure**, **city_destination**, **longitude_destination**, **latitude_destination**, **distance**, **duration**, **n_steps**\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n\nThe last three fields must be left empty as they will be filled with the API outputs.\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "b723a6a2-6504-47f4-9ced-f84617a62dbe",
|
||||
"name": "When clicking ‘Test workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"position": [
|
||||
80,
|
||||
420
|
||||
],
|
||||
"parameters": {},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "3b5fa269-e880-4552-8b39-a9eccd8d0d4d",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
580,
|
||||
0
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1240,
|
||||
"height": 640,
|
||||
"content": "### 2. Loop to collect all driving distances and time \nThis starts by collecting all the routes (Departure, Destination) that need distance and driving time by **truck**.\n\n#### How to setup?\n- **Setup API Credentials**\n 1. Get your free API key: [Open Route API Documentation](https://openrouteservice.org/dev/#/api-docs)\n 2. Fill the API key in the HTTP request node\n 3. Select the driving mode (**driving-car**: individual truck) or (**driving-hgv**: commercial truck)\n- **Load records in the Google Sheet Node**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which you want to record your working sessions\n 4. Map the fields: **city_departure**, **longitude_departure**, **latitude_departure**, **city_destination**, **longitude_destination**, **latitude_destination**, **distance**, **duration**, **n_steps**\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n\nThe last three fields must be left empty as they will be filled with the API outputs.\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "e0e79eea-04f0-462a-a930-01a0df4a0058",
|
||||
"name": "5 sec",
|
||||
"type": "n8n-nodes-base.wait",
|
||||
"position": [
|
||||
1660,
|
||||
440
|
||||
],
|
||||
"webhookId": "1c5df276-94b5-42b8-9748-634e389cadcf",
|
||||
"parameters": {},
|
||||
"typeVersion": 1.1
|
||||
},
|
||||
{
|
||||
"id": "53d7aefb-6ecc-465f-8b1e-6141312a80e8",
|
||||
"name": "Sticky Note4",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
0,
|
||||
660
|
||||
],
|
||||
"parameters": {
|
||||
"width": 580,
|
||||
"height": 380,
|
||||
"content": "### [Check the Tutorial](https://www.youtube.com/watch?v=uJtp2NsmR2E)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"5 sec": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Save Results": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "5 sec",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Collect Routes": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Results": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Save Results",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Loop Over Items": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Request Open Route API",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Request Open Route API": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Results",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Collect Routes",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
***Tags**: Supply Chain, Logistics, Geocoding, Transportation, GPS API*
|
||||
|
||||
### Context
|
||||
|
||||
Hi! I’m Samir — a Supply Chain Engineer and Data Scientist based in Paris, and founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
I help companies improve their **logistics operations** using data, AI, and automation to **reduce costs and minimize environmental footprint**.
|
||||
|
||||

|
||||
|
||||
> Let’s use n8n to analyze geographical data!
|
||||
|
||||
📬 For business inquiries, you can add find me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow is designed for **logistics and transport teams** but also **market analytics experts** that need to process geocoding data (get GPS coordinates from addresses).
|
||||
|
||||

|
||||
|
||||
Ideal for:
|
||||
- Transportation Planning
|
||||
- Supply Chain Network Design
|
||||
- Route optimization studies
|
||||
|
||||

|
||||
|
||||
### How does it work?
|
||||
|
||||
This n8n workflow connects to a **Google Sheet** where you store addresses with country codes, and uses the **OpenRouteService API** to calculate:
|
||||
|
||||
- 📏 GPS Coordinates (longitude, latitude)
|
||||
- 🗺️ Neighbourhood, City and local information
|
||||
|
||||
### Steps:
|
||||
1. ✅ Load addresses with country codes
|
||||
2. 🔁 Loop through each record
|
||||
3. 🚚 Query OpenRouteService
|
||||
4. 🧾 Extract and store results: longitude, latitude, neighbourhood
|
||||
5. 📤 Update the Google Sheet with new values
|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
This workflow is beginner-friendly and requires:
|
||||
|
||||
- A **Google Sheet** with route pairs (departure and destination coordinates)
|
||||
- A free **OpenRouteService API key**
|
||||
[👉 Get one here](https://openrouteservice.org/dev/#/signup)
|
||||
|
||||
### Next Steps
|
||||
|
||||
🗒️ Follow the sticky notes inside the workflow to:
|
||||
|
||||
- Select your sheet
|
||||
- Plug in your API key
|
||||
- Launch the flow!
|
||||
|
||||
[](https://www.youtube.com/watch?v=IlblIlKcL0k)
|
||||
**[🎥 Check the Tutorial](https://www.youtube.com/watch?v=IlblIlKcL0k)**
|
||||
|
||||
🚀 You can customise the workflow to:
|
||||
- Add additional outputs from the API
|
||||
- Connect to your TMS via API or EDI
|
||||
|
||||
*This template was built using n8n v1.93.0*
|
||||
*Submitted: June 1, 2025*
|
||||
@@ -0,0 +1,429 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": ""
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "b1d00cd4-b9e8-4d84-b580-08368fbb9396",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
20,
|
||||
-160
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 540,
|
||||
"height": 640,
|
||||
"content": "### 1. Trigger the workflow\nThis starts by collecting all the addresses (with the country code) that need geocoding information.\n\n#### How to setup?\n\n- **Load records in the Google Sheet Node**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which you want to record your working sessions\n 4. Map the fields: **country**, **address** , **longitude**, **latitude**, **borough**, **neighbourhood**, **localadmin**\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n\nThe last five fields must be left empty as they will be filled with the API outputs.\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "32f84996-0480-4c0b-aab7-3f5ebca9dd4e",
|
||||
"name": "When clicking ‘Test workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"position": [
|
||||
60,
|
||||
260
|
||||
],
|
||||
"parameters": {},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "fe628240-0550-42dd-a375-7ecba134fd6c",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
600,
|
||||
-160
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1120,
|
||||
"height": 640,
|
||||
"content": "### 2. Loop to collect GPS coordinates\n\n#### How to setup?\n- **Setup API Credentials**\n 1. Get your free API key: [Open Route API Documentation](https://openrouteservice.org/dev/#/api-docs)\n 2. Fill the API key in the HTTP request node\n- **Load records in the Google Sheet Node**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which you want to record your working sessions\n 4. List the fields to update: **longitude**, **latitude**, **borough**, **neighbourhood**, **localadmin**\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "eac06796-f40d-4c94-96b2-4ad672053295",
|
||||
"name": "Collect Addresses",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
340,
|
||||
260
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": 1232640606,
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.6
|
||||
},
|
||||
{
|
||||
"id": "13198c2c-cdca-4a5e-893a-1fac24090f33",
|
||||
"name": "Query Open Route API",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
960,
|
||||
280
|
||||
],
|
||||
"parameters": {
|
||||
"url": "https://api.openrouteservice.org/geocode/search",
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"sendHeaders": true,
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "api_key"
|
||||
},
|
||||
{
|
||||
"name": "text",
|
||||
"value": "={{ $json.address }}"
|
||||
},
|
||||
{
|
||||
"name": "boundary.country",
|
||||
"value": "={{ $json.country }}"
|
||||
},
|
||||
{
|
||||
"name": "sources",
|
||||
"value": "openstreetmap"
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json; charset=utf-8"
|
||||
},
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "application/json, application/geo+json, application/gpx+xml, img/png; charset=utf-8"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "d0ad6f7e-8049-45c4-8022-8c16a8f71392",
|
||||
"name": "Loop Over Addresses",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"position": [
|
||||
760,
|
||||
260
|
||||
],
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "c84f27c6-3905-455a-b8a7-bb1d6bffafe0",
|
||||
"name": "Save Results",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
1380,
|
||||
280
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"id": "={{ $('Loop Over Addresses').item.json.id }}",
|
||||
"borough": "={{ $json.borough }}",
|
||||
"latitude": "={{ $json.latitude }}",
|
||||
"longitude": "={{ $json.longitude }}",
|
||||
"localadmin": "={{ $json.localadmin }}",
|
||||
"neighbourhood": "={{ $json.neighbourhood }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "id",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "id",
|
||||
"defaultMatch": true,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "country",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "country",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "address",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "address",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "longitude",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "longitude",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "latitude",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "latitude",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "borough",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "borough",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "neighbourhood",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "neighbourhood",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "localadmin",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "localadmin",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "row_number",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"readOnly": true,
|
||||
"required": false,
|
||||
"displayName": "row_number",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [
|
||||
"id"
|
||||
],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "update",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": 1232640606,
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "",
|
||||
"cachedResultUrl": "",
|
||||
"cachedResultName": ""
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.6
|
||||
},
|
||||
{
|
||||
"id": "6fa85604-4db7-431d-b0fd-b6cbb1773c12",
|
||||
"name": "Extract Results",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"position": [
|
||||
1180,
|
||||
280
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "e4c92077-f294-4e49-8276-20d1441c0c2c",
|
||||
"name": "longitude",
|
||||
"type": "string",
|
||||
"value": "={{ $json.features[0].geometry.coordinates[0] }}"
|
||||
},
|
||||
{
|
||||
"id": "899e5304-e7a1-4ce1-bf9a-f403d74934fb",
|
||||
"name": "latitude",
|
||||
"type": "string",
|
||||
"value": "={{ $json.features[0].geometry.coordinates[1] }}"
|
||||
},
|
||||
{
|
||||
"id": "5b35642b-dda4-42b8-b7bb-1275616bfb59",
|
||||
"name": "borough",
|
||||
"type": "string",
|
||||
"value": "={{ $json.features[0].properties.borough }}"
|
||||
},
|
||||
{
|
||||
"id": "aa74bd9a-f543-48e1-a69b-0e6472bda053",
|
||||
"name": "neighbourhood",
|
||||
"type": "string",
|
||||
"value": "={{ $json.features[0].properties.neighbourhood }}"
|
||||
},
|
||||
{
|
||||
"id": "7f303dc8-4e28-4d46-8e3e-9db00dbcac3a",
|
||||
"name": "localadmin",
|
||||
"type": "string",
|
||||
"value": "={{ $json.features[0].properties.localadmin }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 3.4
|
||||
},
|
||||
{
|
||||
"id": "71c85728-f291-4a67-8d0d-8c7fcd38905d",
|
||||
"name": "5 sec",
|
||||
"type": "n8n-nodes-base.wait",
|
||||
"position": [
|
||||
1560,
|
||||
280
|
||||
],
|
||||
"webhookId": "6ecebf96-14ed-4c7b-b467-eb48c143408e",
|
||||
"parameters": {},
|
||||
"typeVersion": 1.1
|
||||
},
|
||||
{
|
||||
"id": "7ca2b5ea-9af8-425d-958e-2f6ffe01656e",
|
||||
"name": "Sticky Note4",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
20,
|
||||
500
|
||||
],
|
||||
"parameters": {
|
||||
"width": 580,
|
||||
"height": 380,
|
||||
"content": "### [Check the Tutorial](https://www.youtube.com/watch?v=IlblIlKcL0k)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"5 sec": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Addresses",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Save Results": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "5 sec",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract Results": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Save Results",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Collect Addresses": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Addresses",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Loop Over Addresses": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Query Open Route API",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Query Open Route API": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract Results",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Collect Addresses",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
**\*Tags**: AI Agent, MCP Server, n8n API, Monitoring, Debugging, Workflow Analytics, Automation\*
|
||||
|
||||
### Context
|
||||
|
||||
Hi! I’m Samir — a Supply Chain Engineer and Data Scientist based in Paris, and founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
This workflow is part of my latest project: an **AI assistant that automatically analyses n8n workflow executions, detects failures, and identifies root causes** through natural conversation with Claude Desktop.
|
||||
|
||||

|
||||
|
||||
> Turn your automation logs into intelligent conversations with an AI that understands your workflows.
|
||||
|
||||
The idea is to use _Claude Desktop_ to help monitor and debug your workflows deployed in production.
|
||||
|
||||
The workflow shared here is part of the setup.
|
||||
|
||||
📬 For business inquiries, you can find me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This template is designed for **automation engineers**, **data professionals**, and **AI enthusiasts** who manage multiple workflows in **n8n** and want a smarter way to track errors or performance without manually browsing execution logs.
|
||||
|
||||
If you’ve ever discovered a failed workflow hours after it happened — this is for you.
|
||||
|
||||
### What does this workflow do?
|
||||
|
||||
This workflow acts as the **bridge** between your n8n instance and the **Claude MCP Server**.
|
||||
|
||||

|
||||
|
||||
It exposes three main routes that can be triggered via a webhook:
|
||||
|
||||
1. `get_active_workflows` → Fetches all currently active workflows
|
||||
2. `get_workflow_executions` → Retrieves the latest executions and calculates health KPIs
|
||||
3. `get_execution_details` → Extracts detailed information about failed executions for debugging
|
||||
|
||||
Each request is automatically routed and processed, providing Claude with structured execution data for real-time analysis.
|
||||
|
||||
### How does it fit in the overall setup?
|
||||
|
||||
Here’s the complete architecture:
|
||||
|
||||
```
|
||||
Claude Desktop ←→
|
||||
MCP Server ←→
|
||||
n8n Monitor Webhook ←→
|
||||
n8n API
|
||||
```
|
||||
|
||||
- The **MCP Server** (Python-based) communicates with your n8n instance through this workflow.
|
||||
- The **Claude Desktop app** can then query workflow health, execution logs, and error patterns using natural language.
|
||||
- The **n8n workflow** aggregates, cleans, and returns the relevant metrics (failures, success rates, timing, alerts).
|
||||
|
||||
📘 The full concept and architecture are explained in my article published on my blog:
|
||||
👉 [Deploy your AI Assistant to Monitor and Debug n8n Workflows using Claude and MCP](https://towardsdatascience.com/deploy-your-ai-assistant-to-monitor-and-debug-n8n-workflows-using-claude-and-mcp)
|
||||
|
||||
### 🎥 Tutorial
|
||||
|
||||
The full setup tutorial (with source code and demo) is available on YouTube:
|
||||
|
||||
[](https://youtu.be/oJzNnHIusZs)
|
||||
|
||||
### How does it work?
|
||||
|
||||
- 🌐 Webhook Trigger receives the MCP server requests
|
||||
- 🔀 Switch node routes actions based on `"action"` parameter
|
||||
- ⚙️ HTTP Request nodes fetch execution and workflow data via the n8n API
|
||||
- 🧮 A Code node calculates KPIs (success/failure rates, timing, alerts)
|
||||
- 📤 The processed results are returned as JSON for Claude to interpret
|
||||
|
||||
### Example use cases
|
||||
|
||||
Once connected, you can ask Claude questions like:
|
||||
|
||||
- “Show me all workflows that failed in the last 25 executions.”
|
||||
- “Why is my `Bangkok Meetup Scraper` workflow failing?”
|
||||
- “Give me a health report of my n8n instance.”
|
||||
|
||||

|
||||
|
||||
Claude will reply with structured insights, including failure patterns, node diagnostics, and health status indicators (🟢🟡🔴).
|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
You’ll need:
|
||||
|
||||
- A **self-hosted n8n instance**
|
||||
- **Claude Desktop** app installed
|
||||
- The **MCP server source code** (available on my [GitHub](https://github.com/samirsaci/mcp-n8n-monitor))
|
||||
- The **webhook URL** from this workflow is configured in your `.env` file
|
||||
|
||||
Follow the tutorial for more details, don't hesitate to leave your questions in the comment section.
|
||||
|
||||
### Next Steps
|
||||
|
||||
🗒️ Use the sticky notes inside the workflow to:
|
||||
|
||||
- Replace `<YOUR_N8N_INSTANCE>` with your own URL
|
||||
- Test the webhook routes individually using the “Execute Workflow” button
|
||||
- Connect the MCP server and Claude Desktop to start monitoring
|
||||
|
||||
_This template was built using n8n v.116.2_
|
||||
_Submitted: November 2025_
|
||||
@@ -0,0 +1,576 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": ""
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "18768dea-e6ea-444b-9e98-020f24759612",
|
||||
"name": "Route by Action",
|
||||
"type": "n8n-nodes-base.switch",
|
||||
"position": [
|
||||
288,
|
||||
416
|
||||
],
|
||||
"parameters": {
|
||||
"rules": {
|
||||
"values": [
|
||||
{
|
||||
"outputKey": "active_workflows",
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 1,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "74630646-16e0-4070-a3af-a0dd3c851248",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
},
|
||||
"leftValue": "={{ $json.body.action }}",
|
||||
"rightValue": "get_active_workflows"
|
||||
}
|
||||
]
|
||||
},
|
||||
"renameOutput": true
|
||||
},
|
||||
{
|
||||
"outputKey": "executions_worfklows",
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 1,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "b5d83172-406e-4b65-b6e2-9aa82e52656d",
|
||||
"operator": {
|
||||
"name": "filter.operator.equals",
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
},
|
||||
"leftValue": "={{ $json.body.action }}",
|
||||
"rightValue": "get_workflow_executions"
|
||||
}
|
||||
]
|
||||
},
|
||||
"renameOutput": true
|
||||
},
|
||||
{
|
||||
"outputKey": "execution_details",
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 1,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "d473b358-fada-49a2-9221-c35d1675f944",
|
||||
"operator": {
|
||||
"name": "filter.operator.equals",
|
||||
"type": "string",
|
||||
"operation": "equals"
|
||||
},
|
||||
"leftValue": "={{ $json.body.action }}",
|
||||
"rightValue": "get_execution_details"
|
||||
}
|
||||
]
|
||||
},
|
||||
"renameOutput": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "66e80f9f-f38b-4747-a6f5-5cfc9f5396f2",
|
||||
"name": "Extract All Executions",
|
||||
"type": "n8n-nodes-base.splitOut",
|
||||
"position": [
|
||||
736,
|
||||
240
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"fieldToSplitOut": "data"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "2721ed96-bc48-4d36-9182-8ffd9cb3eb32",
|
||||
"name": "Get Last Executions",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
512,
|
||||
240
|
||||
],
|
||||
"parameters": {
|
||||
"url": "=https://<YOUR_N8N_INSTANCE>/api/v1/executions",
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"authentication": "predefinedCredentialType",
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "includeData",
|
||||
"value": "={{false}}"
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "={{ $('Webhook').item.json.body.limit }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"nodeCredentialType": "n8nApi"
|
||||
},
|
||||
"credentials": {
|
||||
"n8nApi": {
|
||||
"id": "tRdkfwbUbfDfqlYo",
|
||||
"name": "n8n account"
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "a19235be-22a1-40ba-b011-af868392d4c1",
|
||||
"name": "Get Active Workflows",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
512,
|
||||
432
|
||||
],
|
||||
"parameters": {
|
||||
"url": "=https://<YOUR_N8N_INSTANCE>/api/v1/workflows",
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"authentication": "predefinedCredentialType",
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "active",
|
||||
"value": "={{true}}"
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "={{ $('Webhook').item.json.body.workflow_id }}"
|
||||
},
|
||||
{
|
||||
"name": "excludePinnedData",
|
||||
"value": "={{true}}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"nodeCredentialType": "n8nApi"
|
||||
},
|
||||
"credentials": {
|
||||
"n8nApi": {
|
||||
"id": "tRdkfwbUbfDfqlYo",
|
||||
"name": "n8n account"
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "e495cbb1-456d-4680-94bb-a33de770d0b9",
|
||||
"name": "Active Workflows",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"position": [
|
||||
960,
|
||||
432
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "d15f58d4-551d-423d-bae7-19e7b8d9f58d",
|
||||
"name": "createdAt",
|
||||
"type": "string",
|
||||
"value": "={{ $json.createdAt }}"
|
||||
},
|
||||
{
|
||||
"id": "37bff539-0f27-4f40-8235-789910e8c5f8",
|
||||
"name": "updatedAt",
|
||||
"type": "string",
|
||||
"value": "={{ $json.updatedAt }}"
|
||||
},
|
||||
{
|
||||
"id": "29d013e7-127b-41ec-ab1d-4deef3c040f4",
|
||||
"name": "id",
|
||||
"type": "string",
|
||||
"value": "={{ $json.id }}"
|
||||
},
|
||||
{
|
||||
"id": "aee15b56-0237-45cf-8a29-f5d24eb7db15",
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"value": "={{ $json.name }}"
|
||||
},
|
||||
{
|
||||
"id": "78d99983-8baa-4b07-b357-bcc7a0ecfa4d",
|
||||
"name": "isArchived",
|
||||
"type": "string",
|
||||
"value": "={{ $json.isArchived }}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 3.4
|
||||
},
|
||||
{
|
||||
"id": "6f9fe67b-0557-4656-bbcd-9cd6dcd8be15",
|
||||
"name": "Webhook",
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"position": [
|
||||
64,
|
||||
432
|
||||
],
|
||||
"webhookId": "8d8ea5d4-986e-46f3-adee-11eda64f8f60",
|
||||
"parameters": {
|
||||
"path": "8d8ea5d4-986e-46f3-adee-11eda64f8f60",
|
||||
"options": {
|
||||
"rawBody": false
|
||||
},
|
||||
"httpMethod": "POST",
|
||||
"responseMode": "responseNode"
|
||||
},
|
||||
"typeVersion": 2.1
|
||||
},
|
||||
{
|
||||
"id": "dc6154ac-1d58-4f25-b950-31cf386cdccd",
|
||||
"name": "Respond to Webhook",
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"position": [
|
||||
1184,
|
||||
432
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"respondWith": "allIncomingItems"
|
||||
},
|
||||
"typeVersion": 1.4
|
||||
},
|
||||
{
|
||||
"id": "c0ebfa4a-82ac-4489-8b7e-ae1fda603c6a",
|
||||
"name": "Get Executions Details (Status = Error)",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
512,
|
||||
624
|
||||
],
|
||||
"parameters": {
|
||||
"url": "=https://<YOUR_N8N_INSTANCE>/api/v1/executions",
|
||||
"options": {},
|
||||
"sendQuery": true,
|
||||
"authentication": "predefinedCredentialType",
|
||||
"queryParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "includeData",
|
||||
"value": "={{true}}"
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"value": "={{ $('Webhook').item.json.body.limit }}"
|
||||
},
|
||||
{
|
||||
"name": "workflowId",
|
||||
"value": "={{ $('Webhook').item.json.body.workflow_id }}"
|
||||
},
|
||||
{
|
||||
"name": "status",
|
||||
"value": "error"
|
||||
}
|
||||
]
|
||||
},
|
||||
"nodeCredentialType": "n8nApi"
|
||||
},
|
||||
"credentials": {
|
||||
"n8nApi": {
|
||||
"id": "tRdkfwbUbfDfqlYo",
|
||||
"name": "n8n account"
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "f871af75-b380-4470-aeaf-ce5dd7795c48",
|
||||
"name": "Processing Audit",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
960,
|
||||
240
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "const executions = $input.all().map(item => item.json);\n\n// Basic metrics \nconst totalExecutions = executions.length;\nconst failedExecutions = executions.filter(exec => exec.status === 'error');\nconst successfulExecutions = executions.filter(exec => exec.status === 'success');\nconst failedCount = failedExecutions.length;\nconst successCount = successfulExecutions.length;\nconst failureRate = totalExecutions > 0 ? ((failedCount / totalExecutions) * 100).toFixed(2) : 0;\n\n// All the unique workflows that had failures\nconst workflowsWithFailures = [...new Set(failedExecutions.map(exec => exec.workflowId))];\nconst workflowsWithFailuresCount = workflowsWithFailures.length;\n\n// Group failed executions by workflow\nconst failedByWorkflow = {};\nfailedExecutions.forEach(exec => {\n if (!failedByWorkflow[exec.workflowId]) {\n failedByWorkflow[exec.workflowId] = {\n workflowId: exec.workflowId,\n failures: [],\n failureCount: 0\n };\n }\n failedByWorkflow[exec.workflowId].failures.push({\n id: exec.id,\n startedAt: exec.startedAt,\n stoppedAt: exec.stoppedAt,\n mode: exec.mode\n });\n failedByWorkflow[exec.workflowId].failureCount++;\n});\n\n// Calculate execution modes distribution\nconst executionModes = executions.reduce((acc, exec) => {\n acc[exec.mode] = (acc[exec.mode] || 0) + 1;\n return acc;\n}, {});\n\n// Get all unique workflows\nconst allWorkflows = [...new Set(executions.map(exec => exec.workflowId))];\nconst totalWorkflowsExecuted = allWorkflows.length;\n\n// Metrics per workflow\nconst workflowMetrics = {};\nallWorkflows.forEach(workflowId => {\n const workflowExecs = executions.filter(exec => exec.workflowId === workflowId);\n const workflowFailed = workflowExecs.filter(exec => exec.status === 'error').length;\n const workflowSuccess = workflowExecs.filter(exec => exec.status === 'success').length;\n \n workflowMetrics[workflowId] = {\n workflowId: workflowId,\n totalExecutions: workflowExecs.length,\n successfulExecutions: workflowSuccess,\n failedExecutions: workflowFailed,\n successRate: workflowExecs.length > 0 ? \n ((workflowSuccess / workflowExecs.length) * 100).toFixed(2) + '%' : '0%',\n failureRate: workflowExecs.length > 0 ? \n ((workflowFailed / workflowExecs.length) * 100).toFixed(2) + '%' : '0%',\n lastExecution: workflowExecs[0]?.startedAt || null,\n executionModes: workflowExecs.reduce((acc, exec) => {\n acc[exec.mode] = (acc[exec.mode] || 0) + 1;\n return acc;\n }, {})\n };\n});\n\n// Sort workflows by failure rate (highest rate first)\nconst workflowsByFailureRate = Object.values(workflowMetrics)\n .sort((a, b) => parseFloat(b.failureRate) - parseFloat(a.failureRate))\n .slice(0, 10); // Top 10 problematic workflows\n\n// Calculate time-based metrics\nconst now = new Date();\nconst executionsByHour = {};\n\nexecutions.forEach(exec => {\n const startTime = new Date(exec.startedAt);\n const hourKey = startTime.toISOString().slice(0, 13) + ':00'; // Round to hour\n \n if (!executionsByHour[hourKey]) {\n executionsByHour[hourKey] = {\n total: 0,\n success: 0,\n error: 0\n };\n }\n \n executionsByHour[hourKey].total++;\n if (exec.status === 'success') {\n executionsByHour[hourKey].success++;\n } else if (exec.status === 'error') {\n executionsByHour[hourKey].error++;\n }\n});\n\n// Calculate average execution time for finished executions\nconst executionTimes = executions\n .filter(exec => exec.finished && exec.startedAt && exec.stoppedAt)\n .map(exec => {\n const start = new Date(exec.startedAt);\n const stop = new Date(exec.stoppedAt);\n return (stop - start) / 1000; // ms to seconds\n });\n\nconst avgExecutionTime = executionTimes.length > 0 \n ? (executionTimes.reduce((a, b) => a + b, 0) / executionTimes.length).toFixed(2)\n : 0;\n\nconst maxExecutionTime = executionTimes.length > 0 \n ? Math.max(...executionTimes).toFixed(2)\n : 0;\n\nconst minExecutionTime = executionTimes.length > 0 \n ? Math.min(...executionTimes).toFixed(2)\n : 0;\n\n// Find most recent failures\nconst recentFailures = failedExecutions\n .sort((a, b) => new Date(b.startedAt) - new Date(a.startedAt))\n .slice(0, 5)\n .map(exec => ({\n id: exec.id,\n workflowId: exec.workflowId,\n startedAt: exec.startedAt,\n mode: exec.mode\n }));\n\n// Return all indicators\nconst kpis = {\n summary: {\n totalExecutions: totalExecutions,\n successfulExecutions: successCount,\n failedExecutions: failedCount,\n failureRate: failureRate + '%',\n successRate: (100 - parseFloat(failureRate)).toFixed(2) + '%',\n totalWorkflowsExecuted: totalWorkflowsExecuted,\n workflowsWithFailures: workflowsWithFailuresCount\n },\n \n executionModes: executionModes,\n \n timing: {\n averageExecutionTime: avgExecutionTime + ' seconds',\n maxExecutionTime: maxExecutionTime + ' seconds',\n minExecutionTime: minExecutionLine truncated
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "b0154d4a-b3bf-44b2-9c8f-98be3d0f6d9e",
|
||||
"name": "Split Active Executions",
|
||||
"type": "n8n-nodes-base.splitOut",
|
||||
"position": [
|
||||
736,
|
||||
432
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"fieldToSplitOut": "data"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "6622285f-ae45-4db5-bf59-edcdfe6dbb18",
|
||||
"name": "Details of Executions",
|
||||
"type": "n8n-nodes-base.splitOut",
|
||||
"position": [
|
||||
960,
|
||||
624
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"fieldToSplitOut": "data"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "2dfff967-1d7c-40c8-863e-fe414424d38c",
|
||||
"name": "Video Tutorial",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
1328,
|
||||
112
|
||||
],
|
||||
"parameters": {
|
||||
"color": 4,
|
||||
"width": 464,
|
||||
"height": 352,
|
||||
"content": "📹 **Video Tutorial**\n\nWatch the complete step by step tutorial to implement it:\n\n@[youtube](oJzNnHIusZs)"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "be5cf4f8-2f10-4dc0-bd37-cf24605a85dd",
|
||||
"name": "Main Overview",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-432,
|
||||
112
|
||||
],
|
||||
"parameters": {
|
||||
"width": 464,
|
||||
"height": 552,
|
||||
"content": "## Workflow Monitoring Endpoint \n\n\n### How it works\nThis workflow monitors your n8n instance health through API endpoints. Send POST requests with different actions to get execution metrics, workflow statuses, and failure analysis. Generates comprehensive KPIs including success rates, timing metrics, and identifies problematic workflows.\n\n### Setup steps \n1. Replace `<YOUR_N8N_INSTANCE>` in all HTTP nodes with your instance URL\n2. Configure n8n API credentials for authentication \n3. Test webhook with action parameters:\n - `get_active_workflows`\n - `get_workflow_executions` \n - `get_execution_details`\n4. Set `limit` and `workflow_id` in request body as needed\n\n### Customization\nModify Processing Audit node to adjust failure thresholds (currently 10% triggers alerts) or add custom KPIs for your monitoring needs."
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "99b8bf13-19bd-486b-acfb-0a2a119e5696",
|
||||
"name": "Webhook Entry",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
48,
|
||||
112
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 352,
|
||||
"height": 684,
|
||||
"content": "### Webhook Entry\nReceives POST requests with action parameter to route monitoring requests"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "aa8146aa-fcdd-40ba-84be-9ccf9a7df67b",
|
||||
"name": "API Calls",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
416,
|
||||
112
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 248,
|
||||
"height": 680,
|
||||
"content": "### API Calls\nFetch data from n8n API endpoints"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "c88ac162-c9be-418d-9eea-a2d28fd0e793",
|
||||
"name": "Data Processing",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
688,
|
||||
112
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 432,
|
||||
"height": 680,
|
||||
"content": "### Data Processing\nExtract and analyze execution data to generate KPIs"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "59e8cb24-3441-463f-9d44-155c011ae1a8",
|
||||
"name": "Response",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
1152,
|
||||
112
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 160,
|
||||
"height": 684,
|
||||
"content": "### Response\nReturn results to webhook caller"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"Webhook": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Route by Action",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Route by Action": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get Active Workflows",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Get Last Executions",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"node": "Get Executions Details (Status = Error)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Active Workflows": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Respond to Webhook",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Processing Audit": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Respond to Webhook",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Last Executions": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Extract All Executions",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Active Workflows": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Split Active Executions",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Details of Executions": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Respond to Webhook",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Extract All Executions": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Processing Audit",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Split Active Executions": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Active Workflows",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Executions Details (Status = Error)": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Details of Executions",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
*Tags: Supply Chain, Logistics, AI Agents*
|
||||
|
||||
### Context
|
||||
|
||||
Hey! I’m Samir, a Supply Chain Data Scientist from Paris, and the founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
We design tools to help companies improve their **logistics processes** using data analytics, AI, and automation—to **reduce costs and minimize environmental impacts**.
|
||||
|
||||
>Let’s use N8N to improve logistics operations!
|
||||
|
||||
📬 For business inquiries, you can add me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow template is designed for **logistics or manufacturing operations** that receive orders by email.
|
||||
|
||||

|
||||
|
||||
The example above illustrate the challenge we want to tackle using an **AI Agent** to parse the information and load them in a **Google sheet**.
|
||||
|
||||
If you want to understand how I built this workflow, check my detailed tutorial:
|
||||
|
||||
[](https://www.youtube.com/watch?v=kQ8dO_30SB0)
|
||||
|
||||
|
||||
[🎥 Step-by-Step Tutorial](https://www.youtube.com/watch?v=kQ8dO_30SB0)
|
||||
|
||||
### How does it work?
|
||||
|
||||
The workflow is connected to a **Gmail Trigger** to open all the emails that include *Inbound Order* in their subject.
|
||||
|
||||
The email is parsed by an **AI Agent** equipped with **OpenAI's GPT** to collect all the information.
|
||||
|
||||
The results are pulled in a **Google Sheet**.
|
||||
|
||||

|
||||
|
||||
These orderlines can then be **transferred to warehouse teams** to prepare ***order receiving**.
|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
You’ll need:
|
||||
- **Gmail and Google Drive Accounts** with the API credentials to access it via n8n
|
||||
- An **OpenAI API key** (GPT-4o) for the chat model.
|
||||
- A **Google Sheet** with these columns: **PO_NUMBER, EXPECTED_DELIVERY DATE, SKU_ID, QUANTITY**
|
||||
|
||||
### Next Steps
|
||||
|
||||
Follow the sticky notes in the workflow to configure each node and start using **AI to support your logistic operations**.
|
||||
|
||||
🚀 Curious how N8N can transform your logistics operations?
|
||||
📬 Let’s connect on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Notes
|
||||
|
||||
- An example of email is included in the template so you can try it with your mailbox.
|
||||
|
||||
*This workflow was built using N8N version 1.82.1*
|
||||
*Submitted: March 28, 2025*
|
||||
+351
@@ -0,0 +1,351 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": "6a5e68bcca67c4cdb3e0b698d01739aea084e1ec06e551db64aeff43d174cb23",
|
||||
"templateCredsSetupCompleted": true
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "bc49829b-45f2-4910-9c37-907271982f14",
|
||||
"name": "Sticky Note3",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-140,
|
||||
320
|
||||
],
|
||||
"parameters": {
|
||||
"width": 780,
|
||||
"height": 540,
|
||||
"content": "### 4. Do you need more details?\nFind a step-by-step guide in this tutorial\n\n[🎥 Watch My Tutorial](https://www.youtube.com/watch?v=kQ8dO_30SB0)"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "40c6e16a-3b4f-4e28-b0a1-7066e0efab5d",
|
||||
"name": "AI Agent",
|
||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||
"position": [
|
||||
-460,
|
||||
-80
|
||||
],
|
||||
"parameters": {
|
||||
"text": "=Email Subject: {{ $json.subject }}\nEmail Body: \n{{ $json.text }}",
|
||||
"options": {
|
||||
"systemMessage": "=You are an assistant that processes emails related to inbound orders from Hermas.\n\nEach email has the subject line containing a purchase order reference (e.g., \"PO45231\").\nIn the email body, you will find:\n\nAn expected delivery date, typically in formats like 27/03/2025 or 2025-03-27.\n\nOne or more order lines, where each line contains:\n\nAn SKU (e.g., HERM-SHOE-001)\n\nA quantity (e.g., 120)\n\nYour goal is to extract the following fields:\n\npurchase_order: The PO number from the subject line (e.g., PO45231)\n\nexpected_delivery_date: In ISO format (e.g., 2025-03-27)\n\nlines: A list of objects with sku and quantity for each order line\n\nReturn your output strictly as a valid JSON object using the format below."
|
||||
},
|
||||
"promptType": "define",
|
||||
"hasOutputParser": true
|
||||
},
|
||||
"typeVersion": 1.8
|
||||
},
|
||||
{
|
||||
"id": "e9cb7bb1-40e7-463e-8b3f-417602338e5c",
|
||||
"name": "OpenAI Chat Model",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"position": [
|
||||
-520,
|
||||
120
|
||||
],
|
||||
"parameters": {
|
||||
"model": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gpt-4o-mini"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "468bdb39-223f-4bae-8bdb-a72272ab57c3",
|
||||
"name": "Structured Output Parser",
|
||||
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
|
||||
"position": [
|
||||
-180,
|
||||
120
|
||||
],
|
||||
"parameters": {
|
||||
"jsonSchemaExample": "{\n \"purchase_order\": \"PO45231\",\n \"expected_delivery_date\": \"2025-03-27\",\n \"lines\": [\n { \"sku\": \"HERM-SHOE-001\", \"quantity\": 120 },\n { \"sku\": \"HERM-BAG-032\", \"quantity\": 45 },\n { \"sku\": \"HERM-WATCH-105\", \"quantity\": 30 },\n { \"sku\": \"HERM-SCARF-018\", \"quantity\": 80 }\n ]\n}\n"
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "667a8d43-1ce5-4ec8-871a-26007356a89e",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-1000,
|
||||
-460
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 380,
|
||||
"height": 720,
|
||||
"content": "### 1. Workflow Trigger with Gmail Trigger\nThe workflow is triggered by a new email received in your Gmail mailbox. \nIf the subject includes the string \"Inbound Order\" we proceed, if not we do nothing.\n\n#### How to setup?\n- **Gmail Trigger Node:** set up your Gmail API credentials\n[Learn more about the Gmail Trigger Node](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.gmailtrigger)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "e1e2d95a-9bbd-4bd5-92ec-7a4835db21a2",
|
||||
"name": "Sticky Note2",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-600,
|
||||
-460
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 660,
|
||||
"height": 720,
|
||||
"content": "### 2. AI Agent equipped with the query tool\nThe email body and subject are sent to the AI agent for parsing. The results include the **PO Number**, **expected delivery date** and all the order lines with **SKU ID** and **order quantity**. Outputs are formatted by the code node to fit in a Google Sheet.\n\n#### How to setup?\n- **AI Agent with the Chat Model**:\n 1. Add a **chat model** with the required credentials *(Example: Open AI 4o-mini)*\n 2. Adapt the system prompt to the format of your emails\n [Learn more about the AI Agent Node](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent)"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "53375c17-a36c-431e-9ba6-07a9a84fc4c9",
|
||||
"name": "Sticky Note4",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
80,
|
||||
-460
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 460,
|
||||
"height": 540,
|
||||
"content": "### 3. Store the orderlines in a Google Sheet\nThe table generated by the **code node** includes all the order lines with the **PO Number** and the **expected delivery date**. This **Google Sheet Node** loads the content in a Google Sheet.\n\n#### How to setup?\n- **Add Results in Google Sheets**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which the vocabulary list is stored\n 4. Create the columns: **PO_NUMBER, EXPECTED_DELIVERY DATE, SKU_ID, QUANTITY**\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "776cfc0e-264b-44cc-b534-dc387b0c9fce",
|
||||
"name": "Store Purchase Order Lines",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
180,
|
||||
-80
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"SKU_ID": "={{ $json.sku }}",
|
||||
"QUANTITY": "={{ $json.quantity }}",
|
||||
"PO_NUMBER": "={{ $json.purchase_order }}",
|
||||
"EXPECTED_DELIVERY DATE": "={{ $json.expected_delivery_date }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "PO_NUMBER",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "PO_NUMBER",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "EXPECTED_DELIVERY DATE",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "EXPECTED_DELIVERY DATE",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "SKU_ID",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "SKU_ID",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "QUANTITY",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "QUANTITY",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "1HnaJJ-DqzqgWJo2YwQDcgB6BgWiU6eMlnGvv4kapubg",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.5
|
||||
},
|
||||
{
|
||||
"id": "d5c52625-fef2-47a9-b2a4-bf005d8b9e05",
|
||||
"name": "Email Received",
|
||||
"type": "n8n-nodes-base.gmailTrigger",
|
||||
"position": [
|
||||
-980,
|
||||
-80
|
||||
],
|
||||
"parameters": {
|
||||
"simple": false,
|
||||
"filters": {},
|
||||
"options": {},
|
||||
"pollTimes": {
|
||||
"item": [
|
||||
{
|
||||
"mode": "everyMinute"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "6dc9e5cc-9ab3-469c-ad93-e0e7817ccbf7",
|
||||
"name": "Is PO?",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"position": [
|
||||
-760,
|
||||
-80
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 2,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "f300ae2b-5de4-4efc-88ae-130a957588cb",
|
||||
"operator": {
|
||||
"type": "string",
|
||||
"operation": "contains"
|
||||
},
|
||||
"leftValue": "={{ $json.subject }}",
|
||||
"rightValue": "Inbound Order"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 2.2
|
||||
},
|
||||
{
|
||||
"id": "385db736-0867-46b9-9274-380e7c255fc4",
|
||||
"name": "Format Purchase Order Lines",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
-120,
|
||||
-80
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "const {purchase_order, expected_delivery_date, lines} = $input.first().json.output;\n\nreturn lines.map( line => ({\n json: {\n purchase_order,\n expected_delivery_date,\n sku: line.sku,\n quantity: line.quantity\n }\n}))\n"
|
||||
},
|
||||
"typeVersion": 2
|
||||
},
|
||||
{
|
||||
"id": "b2e39591-70be-4d7f-a5d4-1505741d6310",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-1000,
|
||||
320
|
||||
],
|
||||
"parameters": {
|
||||
"width": 780,
|
||||
"height": 720,
|
||||
"content": "### Test the workflow with this email!\n\n#### How?\n1. Send this email to the Gmail box you set up in your credentials.\n2. Click on Test workflow\n\n### Email\n**Email Subject:** Inbound Order PO45231 – Expected Delivery on 2025-03-27\n\n**Email Body:** \nDear LogiGreen Team,\n\nPlease find below the details of the upcoming inbound order.\n\nPurchase Order: PO45231\nExpected Delivery Date: 27/03/2025\n\nOrder Lines:\n\nSKU: HERM-SHOE-001 — Qty: 120\n\nSKU: HERM-BAG-032 — Qty: 45\n\nSKU: HERM-WATCH-105 — Qty: 30\n\nSKU: HERM-SCARF-018 — Qty: 80\n\nLet us know if you need any additional details.\n\nBest regards,\nSophie Lambert\nAdmin Assistant – Hermas Logistics\n📞 +33 1 23 45 67 89 78 84\n✉️ sophie.lambert@hermas.com\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"Is PO?": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"AI Agent": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Format Purchase Order Lines",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Email Received": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Is PO?",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"OpenAI Chat Model": {
|
||||
"ai_languageModel": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent",
|
||||
"type": "ai_languageModel",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Structured Output Parser": {
|
||||
"ai_outputParser": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent",
|
||||
"type": "ai_outputParser",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Format Purchase Order Lines": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Store Purchase Order Lines",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
**\*Tags**: AI Agent, Supply Chain, Logistics, Route Planning, Transportation, GPS API\*
|
||||
|
||||
### Context
|
||||
|
||||
Hi! I’m Samir — a Supply Chain Engineer and Data Scientist based in Paris, and founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
I help companies improve their **logistics operations** using data, AI, and automation to **reduce costs and minimize environmental footprint**.
|
||||
|
||||
> Let’s use n8n to automate order management for transportation companies!
|
||||
|
||||
📬 For business inquiries, you can add find me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow is designed for **small transportation companies and carriers** that handle their delivery request **by email**.
|
||||
|
||||

|
||||
|
||||
Two **AI Agent nodes** and a connection to **Openroute Service API** are used to parse the content of pickup requests
|
||||
|
||||

|
||||
|
||||
The results include **driving time** and **driving distance** in a comprehensive and concise reply generated by the second AI Agent.
|
||||
|
||||
### How does it work?
|
||||
|
||||
This n8n workflow automates the processing of **shipment request emails** and enriches them with **distance and driving time** using the OpenRouteService API.
|
||||
|
||||
- 📥 Extract structured logistics data (pickup, delivery, contact) using an AI Agent
|
||||
- 📌 Geocode pickup and delivery addresses into GPS coordinates
|
||||
- 🚚 Query OpenRouteService using the **truck (driving-hgv)** profile
|
||||
- 📏 Get the driving distance (in km) and estimated time (in minutes)
|
||||
- 📤 Store all data in a connected Google Sheet
|
||||
- 📧 Send back a professional confirmation email to the customer
|
||||
|
||||
### Steps:
|
||||
|
||||
1. 📩 Trigger the workflow from a new Gmail message
|
||||
2. 🧠 Use the AI Agent to extract structured data from the email
|
||||
3. 📊 Record and enrich shipment details in a Google Sheet
|
||||
4. 🗺️ Geocode addresses into coordinates
|
||||
5. 🚦 Get driving distance & time via OpenRouteService
|
||||
6. 📬 Generate and send back a confirmation email using AI
|
||||
|
||||

|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
This workflow requires:
|
||||
|
||||
- A **Gmail account** to receive shipment requests
|
||||
- A **Google Sheet** to log and update shipment info
|
||||
- A **free OpenRouteService API key**
|
||||
[👉 Get one here](https://openrouteservice.org/dev/#/signup)
|
||||
- OpenAI API access to use the **AI Agent node**
|
||||
|
||||
### Next Steps
|
||||
|
||||
🗒️ Follow the sticky notes inside the workflow canvas to:
|
||||
|
||||
- Plug your Gmail and Google credentials
|
||||
- Add your OpenRouteService API key
|
||||
- Test by sending a sample email and see the response!
|
||||
|
||||
[](https://youtu.be/0InkUBOUMQQ)
|
||||
[🎥 Check the Tutorial](https://youtu.be/0InkUBOUMQQ)
|
||||
|
||||
_This template was built using n8n v1.93.0_
|
||||
_Submitted: June 5, 2025_
|
||||
+1595
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,71 @@
|
||||
_Tags: Productivity, Education, Learning, Language_
|
||||
|
||||
### Context
|
||||
|
||||
I’m a **Supply Chain Data Scientist from Paris** who lived six years in China — and yes, **learning Mandarin while working full-time was tough**.
|
||||
|
||||

|
||||
|
||||
Learning Mandarin as an adult can be very difficult, especially if you have a full-time job. With AI, you can now have a **Chinese tutor available 24/7** on your phone — no excuses left!
|
||||
|
||||
It is with this spirit that I designed this workflow to support fellow Mandarin learners with a **Chinese Teacher powered by GPT-4o**.
|
||||
|
||||
>Boost your **language skills** with AI using N8N!
|
||||
|
||||
📬 For business inquiries, you can add me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow template is designed for **language learners** and **educators** who need support to learn a vocabulary list in Mandarin (or any other language) using **Open AI GPT-4o**, an **AI agent** and a **Telegram Bot** to interact with users.
|
||||
|
||||

|
||||
|
||||
For the vocabulary list, you can use another template shared in my profile [🉑 Generate Anki Flash Cards for Language Learning with Google Translate and GPT-4o](https://n8n.io/workflows/3195-generate-anki-flash-cards-for-language-learning-with-google-translate-and-gpt-4o/) to generate the Google Sheet needed in this workflow.
|
||||
|
||||
### How does it work?
|
||||
|
||||
The workflow loads a **vocabulary list** stored in your **Google Sheet**.
|
||||
|
||||

|
||||
|
||||
The bot will:
|
||||
|
||||
1. 📥 Load your vocabulary list from Google Sheets
|
||||
2. 🧠 Generate multiple-choice questions with GPT-4o
|
||||
3. ✅ Evaluate your answer and give instant feedback
|
||||
4. 🔁 Loop to the next word until you're fluent
|
||||
|
||||

|
||||
|
||||
These fields will be automatically added to a Google Sheet, ready to be loaded in Anki to create flash cards.
|
||||
|
||||
### What do I need to start?
|
||||
|
||||
This workflow does not require any advanced programming skills.
|
||||
|
||||
#### Prerequisite
|
||||
|
||||
- A **Google Drive Account** with a folder including a Google Sheet filled with the vocabulary list you want to learn.
|
||||
- API Credentials: **Open AI API** for the chat model, **Google Drive API** and **Google Sheets API** activated with OAuth2 credentials
|
||||
- A **Telegram Bot** with its token recorded in the Telegram Node Credentials
|
||||
- **A Google Sheet** with two columns (initialText: words in your own language, translatedText: words in the target language)
|
||||
|
||||
### Next Steps
|
||||
|
||||
Follow the sticky notes to set up the parameters inside each node and get ready to pump your learning skills.
|
||||
|
||||
[](https://youtu.be/MQV8wDSug7M)
|
||||
|
||||
[🎥 Watch My Tutorial](https://youtu.be/MQV8wDSug7M)
|
||||
|
||||
🚀 Curious how N8N can supercharge learning or supply chain?
|
||||
📬 Let’s connect on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
---
|
||||
|
||||
### Notes
|
||||
|
||||
- This workflow can be used **for any language**. In the AI Agent prompt, you just need to replace Chinese with your language.
|
||||
|
||||
This workflow has been created with N8N 1.82.1
|
||||
Submitted: March 23th, 2025
|
||||
@@ -0,0 +1,268 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": "6a5e68bcca67c4cdb3e0b698d01739aea084e1ec06e551db64aeff43d174cb23",
|
||||
"templateCredsSetupCompleted": true
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "bc49829b-45f2-4910-9c37-907271982f14",
|
||||
"name": "Sticky Note3",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-1500,
|
||||
-520
|
||||
],
|
||||
"parameters": {
|
||||
"width": 780,
|
||||
"height": 540,
|
||||
"content": "### 3. Do you need more details?\nFind a step-by-step guide in this tutorial\n\n[🎥 Watch My Tutorial](https://youtu.be/MQV8wDSug7M)"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "80af5237-9046-4b40-ac7c-167d8e0a490f",
|
||||
"name": "AI Agent",
|
||||
"type": "@n8n/n8n-nodes-langchain.agent",
|
||||
"notes": "Pinyin + Example",
|
||||
"position": [
|
||||
-2140,
|
||||
-140
|
||||
],
|
||||
"parameters": {
|
||||
"text": "={{ $('Telegram Trigger').item.json.message.text }}",
|
||||
"options": {
|
||||
"systemMessage": "=# Context\nYou are an AI-powered language tutor designed to help {{ $('Telegram Trigger').item.json.message.chat.first_name }} practice Chinese vocabulary efficiently. \n\n# Role\nYour primary role is to generate interactive Multiple-Choice Questions (MCQs) and evaluate the user's responses.\n\n# Types of Exercises\n- MCQ: Provide an English word and four Chinese answer choices, one correct and three incorrect.\n\n# Rules for MCQ Generation\n1. Select a random **Chinese word** from this list {{ $json.targetLanguage }}\n2. Randomly select **three incorrect Chinese options** from the list or outside the list.\n3. **Do NOT mark the correct answer with ✅** in the question.\n4. Present the question in the following format:\nExample Question Format:\nWhat is the correct translation for \"Warehouse\"?\nA) 运输\nB) 仓库 \nC) 合同\nD) 投标\n5. Ask the user to respond with **A, B, C, or D**.\n\n# Evaluating User Responses:\n1. **Wait for the user's answer. Do NOT assume correctness before checking.**\n2. If the user selects the correct answer:\n- Respond positively: \"Great job! ✅ [Correct Answer] [Correct Answer's Pinyin] means [English Meaning].\"\n3. If the user selects the wrong answer:\n- Provide corrective feedback: \"Oops! ❌ The correct answer was [Correct Answer] ([English Meaning]).\"\n4. If the user provides an **invalid response** (e.g., \"Hello\"), ask them to respond with **A, B, C, or D**.\n\n# Post-Evaluation:\n- After giving feedback, always generate another question. Do not ask the user if he wants another question\n\n# Behavior & Tone\n- Be engaging and encouraging.\n- Ensure clarity in feedback.\n- Guide the user patiently if they provide invalid inputs."
|
||||
},
|
||||
"promptType": "define",
|
||||
"hasOutputParser": true
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.7
|
||||
},
|
||||
{
|
||||
"id": "8b35027e-ec5b-4c3e-9a5b-2780b6c40223",
|
||||
"name": "OpenAI Chat Model",
|
||||
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
||||
"position": [
|
||||
-2180,
|
||||
100
|
||||
],
|
||||
"parameters": {
|
||||
"model": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gpt-4o-mini"
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 1.2
|
||||
},
|
||||
{
|
||||
"id": "688d6882-4930-407d-bf58-5f6add8eb159",
|
||||
"name": "Simple Memory",
|
||||
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
|
||||
"position": [
|
||||
-2000,
|
||||
140
|
||||
],
|
||||
"parameters": {
|
||||
"sessionKey": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
|
||||
"sessionIdType": "customKey"
|
||||
},
|
||||
"typeVersion": 1.3
|
||||
},
|
||||
{
|
||||
"id": "33f4a062-73f9-4a99-abca-1184ef2c2a41",
|
||||
"name": "Telegram Trigger",
|
||||
"type": "n8n-nodes-base.telegramTrigger",
|
||||
"position": [
|
||||
-2960,
|
||||
-140
|
||||
],
|
||||
"webhookId": "88179da7-9927-4bdc-8bd7-78022810b48e",
|
||||
"parameters": {
|
||||
"updates": [
|
||||
"message"
|
||||
],
|
||||
"additionalFields": {}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.1
|
||||
},
|
||||
{
|
||||
"id": "af385807-d024-477e-9a42-c195043e95da",
|
||||
"name": "Retrive Vocabulary",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
-2700,
|
||||
-140
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": 0,
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "=",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.5
|
||||
},
|
||||
{
|
||||
"id": "3ab67ca5-9839-4fa6-bfc1-4dbbaf5593fc",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-3000,
|
||||
-520
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 680,
|
||||
"height": 540,
|
||||
"content": "### 1. Workflow Trigger with Telegram Message\n1. The workflow is triggered by a user message. \n2. The second node retrieves the vocabulary list from a Google Sheet.\n3. The third node combines all the words in Chinese and English in two distinctive lists.\n\n#### How to setup?\n- **Telegram Node:** set up your telegram bot credentials\n[Learn more about the Telegram Trigger Node](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.telegramtrigger/)\n- **Retrieve Vocabulary from a Google Sheet Node**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which you have stored your vocabulary list\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "740a2d04-46fe-41f1-b887-f88f3e23c50d",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
-2300,
|
||||
-520
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 760,
|
||||
"height": 780,
|
||||
"content": "### 2. Conversational AI Agent\nThe AI agent will take as inputs the two vocabulary lists and user's message to asks questions and process answers. Conversations are recorded by chat id; each user has its own conversation with the bot.\n\n#### How to setup?\n- **Telegram Nodes:** set up your telegram bot credentials\n[Learn more about the Telegram Trigger Node](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.telegramtrigger/)\n- **AI Agent with the Chat Model**:\n 1. Add a chat model with the required credentials *(Example: Open AI 4o-mini)*\n 2. Adapt the system prompt with the **target learning language** and the format of the question you want to have.\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "e92a55dc-6d9d-4008-bb40-72a7f2dd470c",
|
||||
"name": "Aggregate Vocabulary Lists",
|
||||
"type": "n8n-nodes-base.aggregate",
|
||||
"position": [
|
||||
-2460,
|
||||
-140
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"fieldsToAggregate": {
|
||||
"fieldToAggregate": [
|
||||
{
|
||||
"renameField": true,
|
||||
"outputFieldName": "initialLanguage",
|
||||
"fieldToAggregate": "initialText"
|
||||
},
|
||||
{
|
||||
"renameField": true,
|
||||
"outputFieldName": "targetLanguage",
|
||||
"fieldToAggregate": "translatedText"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "18b29677-cfc0-4817-9321-35090a3fda2e",
|
||||
"name": "Answer to the User",
|
||||
"type": "n8n-nodes-base.telegram",
|
||||
"position": [
|
||||
-1740,
|
||||
-140
|
||||
],
|
||||
"webhookId": "=",
|
||||
"parameters": {
|
||||
"text": "={{ $json.output }}",
|
||||
"chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
|
||||
"additionalFields": {
|
||||
"appendAttribution": false
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.2
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"AI Agent": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Answer to the User",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Simple Memory": {
|
||||
"ai_memory": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent",
|
||||
"type": "ai_memory",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Telegram Trigger": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Retrive Vocabulary",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"OpenAI Chat Model": {
|
||||
"ai_languageModel": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent",
|
||||
"type": "ai_languageModel",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Retrive Vocabulary": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Aggregate Vocabulary Lists",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Aggregate Vocabulary Lists": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "AI Agent",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
*Tags: Accessibility, SEO, Blogging, Marketing, Automation, AI, Web Auditing*
|
||||
|
||||
### Context
|
||||
|
||||
Hey! I’m Samir, a Supply Chain Engineer and Data Scientist from Paris, and the founder of [LogiGreen Consulting](https://logi-green.com).
|
||||
|
||||
In my personal blog, I share insights on how to use **AI, automation, and data analytics** to improve logistics, operations, and digital sustainability practices.
|
||||
|
||||

|
||||
|
||||
> Have you heard about accessibility?
|
||||
|
||||
In this workflow, I use n8n to improve the quality of alternative texts for images **on my personal website.**
|
||||
|
||||
📬 For business inquiries, you can connect with me on [LinkedIn](https://www.linkedin.com/in/samir-saci)
|
||||
|
||||
### Who is this template for?
|
||||
|
||||
This workflow is for:
|
||||
- **Bloggers** and **website owners** who want to **improve accessibility**
|
||||
- **SEO professionals** looking to boost page performance
|
||||
- **Web developers** and **product teams** automating web audits
|
||||
|
||||
|
||||
### What does it do?
|
||||
|
||||
This n8n workflow:
|
||||
1. 🔍 Downloads the HTML of a blog or web page
|
||||
2. 🖼️ Extracts all `<img>` tags and their `alt` attributes
|
||||
3. 📉 Detects missing or too-short alt texts
|
||||
4. 🤖 Sends those images to **GPT-4o (with vision)** to generate new alt descriptions
|
||||
5. 📄 Saves the results into a **Google Sheet**, updating the alt text when needed
|
||||
|
||||
|
||||
### How it works
|
||||
|
||||

|
||||
|
||||
1. **Set a page URL** using the `Set` node
|
||||
2. **Download HTML** content
|
||||
3. **Extract image `src` and `alt`** using a `Code` node
|
||||
4. **Store results** in a Google Sheet
|
||||
5. **Filter images** with `altLength < 50`
|
||||
6. **Send image URL** to GPT-4o
|
||||
7. **Update the Google Sheet** with the newly generated `newAlt` text
|
||||
|
||||
The AI alt texts are concise, descriptive, and accessibility-compliant.
|
||||
|
||||
### What do I need to get started?
|
||||
|
||||
You’ll need:
|
||||
- A **Google Sheet** to store the audit results
|
||||
- An **OpenAI account with GPT-4o access**
|
||||
|
||||
### Follow the Guide!
|
||||
Follow the sticky notes in the workflow or check my tutorial to configure each node and start using AI to **improve the accessibility of your website.**
|
||||
|
||||
[](https://www.youtube.com/watch?v=LwTIro6Rapk)
|
||||
|
||||
🎥 [Watch My Tutorial](https://www.youtube.com/watch?v=LwTIro6Rapk)
|
||||
|
||||
### Notes
|
||||
|
||||
- GPT-generated alt texts are limited to ~125–150 characters for best results
|
||||
- Use this to comply with **WCAG** and improve Google indexing
|
||||
- Easily adapt it to audit multiple domains or e-commerce catalogues
|
||||
|
||||
*This workflow was built using n8n version 1.85.4*
|
||||
*Submitted: April 21, 2025*
|
||||
@@ -0,0 +1,561 @@
|
||||
{
|
||||
"meta": {
|
||||
"instanceId": ""
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "95963c27-45a3-4eaa-8120-03ad63e28608",
|
||||
"name": "Sticky Note4",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
1088,
|
||||
0
|
||||
],
|
||||
"parameters": {
|
||||
"width": 460,
|
||||
"height": 292,
|
||||
"content": "@[youtube](LwTIro6Rapk)"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "c502c85a-7de3-406b-968e-4f9c6fa18e72",
|
||||
"name": "When clicking ‘Test workflow’",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"position": [
|
||||
0,
|
||||
320
|
||||
],
|
||||
"parameters": {},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "94701dc7-eac1-42c3-8c7c-d1198aa4a0e0",
|
||||
"name": "Loop Over Items",
|
||||
"type": "n8n-nodes-base.splitInBatches",
|
||||
"position": [
|
||||
1104,
|
||||
848
|
||||
],
|
||||
"parameters": {
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 3
|
||||
},
|
||||
{
|
||||
"id": "cd5c3a71-713c-488a-85bc-85997657bde0",
|
||||
"name": "Sticky Note1",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1040,
|
||||
"height": 460,
|
||||
"content": "### 1. First Block: audit the page to extract all the images with their respective alternative text\nThis workflow sends an HTTP request to collect the HTML processed by the Javascript node to list all the images in the page with their alternative texts. The results are saved in a Google Sheet.\n\n#### How to setup?\n- **Set your page link** in the first node\n- **Record the results in a Google Sheet Node**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which you want to record your working sessions\n 4. Map the fields\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "87938f16-cb0e-47c6-8121-e2a13f716797",
|
||||
"name": "Page Link",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"position": [
|
||||
208,
|
||||
320
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"assignments": {
|
||||
"assignments": [
|
||||
{
|
||||
"id": "e69e5e68-5cd1-4f81-a940-2e5202d5589b",
|
||||
"name": "url",
|
||||
"type": "string",
|
||||
"value": "https://www.samirsaci.com/sustainable-business-strategy-with-data-analytics/"
|
||||
},
|
||||
{
|
||||
"id": "8839ac43-5d6a-4656-b555-714f836fc687",
|
||||
"name": "baseUrl",
|
||||
"type": "string",
|
||||
"value": "https://www.samirsaci.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 3.4
|
||||
},
|
||||
{
|
||||
"id": "f63f64dd-38d7-4124-9faa-66b51a33a596",
|
||||
"name": "Download Results",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
448,
|
||||
848
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "=",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "=",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
}
|
||||
},
|
||||
"typeVersion": 4.5
|
||||
},
|
||||
{
|
||||
"id": "3c94f346-6aad-4e98-bc5e-517e09be190e",
|
||||
"name": "altLength < 50",
|
||||
"type": "n8n-nodes-base.if",
|
||||
"position": [
|
||||
672,
|
||||
848
|
||||
],
|
||||
"parameters": {
|
||||
"options": {},
|
||||
"conditions": {
|
||||
"options": {
|
||||
"version": 2,
|
||||
"leftValue": "",
|
||||
"caseSensitive": true,
|
||||
"typeValidation": "strict"
|
||||
},
|
||||
"combinator": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"id": "a3b0ca70-0496-4966-94fd-f2927ce02ba9",
|
||||
"operator": {
|
||||
"type": "number",
|
||||
"operation": "lt"
|
||||
},
|
||||
"leftValue": "={{ $json.altLength }}",
|
||||
"rightValue": 100
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"typeVersion": 2.2
|
||||
},
|
||||
{
|
||||
"id": "baadbb23-45f0-41ad-aac4-2cc2006b19ea",
|
||||
"name": "Limit records",
|
||||
"type": "n8n-nodes-base.limit",
|
||||
"position": [
|
||||
880,
|
||||
768
|
||||
],
|
||||
"parameters": {
|
||||
"maxItems": 5
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "bc17ce81-d939-4cdf-af45-8c48d1196ce1",
|
||||
"name": "Generate altText",
|
||||
"type": "@n8n/n8n-nodes-langchain.openAi",
|
||||
"position": [
|
||||
1328,
|
||||
768
|
||||
],
|
||||
"parameters": {
|
||||
"text": "Please generate the alternative text (alt text) for this image under 150 characters.\t",
|
||||
"modelId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gpt-4o-2024-05-13",
|
||||
"cachedResultName": "GPT-4O-2024-05-13"
|
||||
},
|
||||
"options": {
|
||||
"maxTokens": 150
|
||||
},
|
||||
"resource": "image",
|
||||
"imageUrls": "={{ $('altLength < 50').item.json.src }}",
|
||||
"operation": "analyze"
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 1.8
|
||||
},
|
||||
{
|
||||
"id": "7a0c7b6c-966e-4d24-b488-914c5ff4b6a8",
|
||||
"name": "Update Results",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
1552,
|
||||
848
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"page": "=",
|
||||
"index": "={{ $('Loop Over Items').item.json.index }}",
|
||||
"newAlt": "={{ $json.content }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "index",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "index",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "page",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "page",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "src",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "src",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "alt",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "alt",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "altLength",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "altLength",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "newAlt",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "newAlt",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "row_number",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": true,
|
||||
"readOnly": true,
|
||||
"required": false,
|
||||
"displayName": "row_number",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [
|
||||
"index"
|
||||
],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "update",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "=",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
}
|
||||
},
|
||||
"typeVersion": 4.5
|
||||
},
|
||||
{
|
||||
"id": "354c203a-e85e-4d05-8de1-6e825053fcf4",
|
||||
"name": "Sticky Note",
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"position": [
|
||||
0,
|
||||
528
|
||||
],
|
||||
"parameters": {
|
||||
"color": 7,
|
||||
"width": 1920,
|
||||
"height": 520,
|
||||
"content": "### 2. SecondBlock: generate alternative text for the image with altLength < 50\nThis workflow sends an HTTP request to collect the HTML processed by the Javascript node to list all the images in the page with their alternative texts. The results are saved in a Google Sheet.\n\n#### How to setup?\n- **Set your page link** in the first node\n- **Record the results in a Google Sheet Node**:\n 1. Add your Google Sheet API credentials to access the Google Sheet file\n 2. Select the file using the list, an URL or an ID\n 3. Select the sheet in which you want to record your working sessions\n 4. Map the fields\n [Learn more about the Google Sheet Node](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets)\n"
|
||||
},
|
||||
"typeVersion": 1
|
||||
},
|
||||
{
|
||||
"id": "33ef44b6-d926-45c8-a932-eff12c4d5ca7",
|
||||
"name": "Store Results",
|
||||
"type": "n8n-nodes-base.googleSheets",
|
||||
"position": [
|
||||
864,
|
||||
320
|
||||
],
|
||||
"parameters": {
|
||||
"columns": {
|
||||
"value": {
|
||||
"alt": "={{ $json.alt }}",
|
||||
"src": "={{ $json.src }}",
|
||||
"page": "={{ $('Page Link').item.json.url }}",
|
||||
"index": "={{ $json.index }}",
|
||||
"altLength": "={{ $json.altLength }}"
|
||||
},
|
||||
"schema": [
|
||||
{
|
||||
"id": "index",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "index",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "page",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"removed": false,
|
||||
"required": false,
|
||||
"displayName": "page",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "src",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "src",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "alt",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "alt",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "altLength",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "altLength",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
},
|
||||
{
|
||||
"id": "newAlt",
|
||||
"type": "string",
|
||||
"display": true,
|
||||
"required": false,
|
||||
"displayName": "newAlt",
|
||||
"defaultMatch": false,
|
||||
"canBeUsedToMatch": true
|
||||
}
|
||||
],
|
||||
"mappingMode": "defineBelow",
|
||||
"matchingColumns": [],
|
||||
"attemptToConvertTypes": false,
|
||||
"convertFieldsToString": false
|
||||
},
|
||||
"options": {},
|
||||
"operation": "append",
|
||||
"sheetName": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "gid=0",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
},
|
||||
"documentId": {
|
||||
"__rl": true,
|
||||
"mode": "list",
|
||||
"value": "=",
|
||||
"cachedResultUrl": "=",
|
||||
"cachedResultName": "="
|
||||
}
|
||||
},
|
||||
"notesInFlow": true,
|
||||
"typeVersion": 4.5
|
||||
},
|
||||
{
|
||||
"id": "37e7d8e0-291d-4c6e-8576-29e5be413400",
|
||||
"name": "Download HTML",
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"position": [
|
||||
432,
|
||||
320
|
||||
],
|
||||
"parameters": {
|
||||
"url": "={{ $json.url }}",
|
||||
"options": {}
|
||||
},
|
||||
"typeVersion": 4.2
|
||||
},
|
||||
{
|
||||
"id": "86641e2b-f1b1-4cc9-ab33-c4181dfe3dd2",
|
||||
"name": "Get Images urls with altText",
|
||||
"type": "n8n-nodes-base.code",
|
||||
"position": [
|
||||
640,
|
||||
320
|
||||
],
|
||||
"parameters": {
|
||||
"jsCode": "const html = $input.first().json.data;\nconst baseUrl = $('Page Link').first().json.baseUrl;\n\nconst imgTagRegex = /<img\\b[^>]*>/gi;\nconst altAttrRegex = /alt\\s*=\\s*[\"']([^\"']*)[\"']/i;\nconst srcAttrRegex = /src\\s*=\\s*[\"']([^\"']*)[\"']/i;\n\nconst imageTags = html.match(imgTagRegex) || [];\n\nconst results = imageTags.map((tag, index) => {\n const altMatch = tag.match(altAttrRegex);\n const srcMatch = tag.match(srcAttrRegex);\n\n let alt = altMatch ? altMatch[1] : '[No alt text]';\n let src = srcMatch ? srcMatch[1] : '[No src]';\n\n // If src is relative, manually join with baseUrl\n if (src !== '[No src]' && !src.startsWith('http')) {\n if (baseUrl.endsWith('/') && src.startsWith('/')) {\n src = baseUrl + src.slice(1);\n } else if (!baseUrl.endsWith('/') && !src.startsWith('/')) {\n src = baseUrl + '/' + src;\n } else {\n src = baseUrl + src;\n }\n }\n\n return {\n index: index + 1,\n src,\n alt,\n altLength: alt.length,\n };\n});\n\nreturn results.map(item => ({ json: item }));"
|
||||
},
|
||||
"typeVersion": 2
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"Page Link": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Download HTML",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Download HTML": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Get Images urls with altText",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Limit records": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Update Results": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Loop Over Items",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"altLength < 50": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Limit records",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Loop Over Items": {
|
||||
"main": [
|
||||
[],
|
||||
[
|
||||
{
|
||||
"node": "Generate altText",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Download Results": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "altLength < 50",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Generate altText": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Update Results",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Get Images urls with altText": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Store Results",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"When clicking ‘Test workflow’": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Page Link",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Download Results",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user