Workflow: develop a modular n8n workflow that supports Code Filter Create Scheduled
Integrations: code, compareDatasets, filter, merge, noOp, notion, scheduleTrigger, set, slack, splitInBatches, stickyNote, webflow

{"nodes": [{"id": "adb2d3bc-c6ab-4bb6-b954-61956ca2836d", "name": "Sticky Note", "type": "n8n-nodes-base.stickyNote", "position": [-1528.3572519550153, 3540], "parameters": {"width": 830.4857444594224, "height": 495.4835100729081, "content": "## Workflow installation\n* Add a \"slug\" text property to each blog post (this parameter will be synced with Webflow and will be used to determine if a post is new or already present in your Webflow collection)\n* Add a \"Sync to Webflow?\" checkbox to each blog post\n* Connect your accounts and run a test to fill Webflow nodes with the right fields\n\n[![image.png](https://i.postimg.cc/xCymVp7w/image.png)](https://postimg.cc/BLbbxpJp)"}, "typeVersion": 1}, {"id": "a5a79fd3-7adb-4e56-8aa7-2fd0cfc22927", "name": "Get simple page data", "type": "n8n-nodes-base.notion", "position": [-80, 4520], "parameters": {"pageId": {"__rl": true, "mode": "id", "value": "={{ $json.id }}"}, "resource": "databasePage", "operation": "get"}, "credentials": {"notionApi": {"id": "rxtaEXgFPg96muhy", "name": "My Notion account"}}, "executeOnce": true, "typeVersion": 2.2}, {"id": "dbb56719-e091-4475-94fb-430cd58ce8bb", "name": "Get all page data", "type": "n8n-nodes-base.notion", "position": [-120, 4840], "parameters": {"pageId": {"__rl": true, "mode": "id", "value": "={{ $json.id }}"}, "simple": false, "resource": "databasePage", "operation": "get"}, "credentials": {"notionApi": {"id": "rxtaEXgFPg96muhy", "name": "My Notion account"}}, "executeOnce": true, "typeVersion": 2.2}, {"id": "af3fd27a-642e-4ec6-bc07-5d02076830e2", "name": "Take cover url", "type": "n8n-nodes-base.set", "position": [100, 4840], "parameters": {"options": {}, "assignments": {"assignments": [{"id": "7f9960fb-9898-4d1a-b4d9-29c95fb7c144", "name": "cover_url", "type": "string", "value": "={{ $json.cover.external.url }}"}]}}, "typeVersion": 3.3}, {"id": "5910292c-2548-4ca2-b7e4-304f99712e8d", "name": "Merge1", "type": "n8n-nodes-base.merge", "position": [320, 4640], "parameters": {"mode": "combine", "options": {}, "combinationMode": "mergeByPosition"}, "typeVersion": 2.1}, {"id": "65c81d79-770c-48d4-97b9-f22328c22465", "name": "Data transporter1", "type": "n8n-nodes-base.noOp", "position": [3220, 4900], "parameters": {}, "typeVersion": 1}, {"id": "1bc81efb-d293-4c97-bcb8-e114de0e482c", "name": "Get all blog posts1", "type": "n8n-nodes-base.notion", "position": [-1220, 4640], "parameters": {"options": {}, "resource": "databasePage", "operation": "getAll", "returnAll": true, "databaseId": {"__rl": true, "mode": "list", "value": "4587b66c-d670-45b5-93f0-69ba1b0f3924", "cachedResultUrl": "https://www.notion.so/4587b66cd67045b593f069ba1b0f3924", "cachedResultName": "My blog"}}, "credentials": {"notionApi": {"id": "rxtaEXgFPg96muhy", "name": "My Notion account"}}, "typeVersion": 2.2}, {"id": "56392232-05c7-477f-911f-7713d6cfa25f", "name": "Is sync checked?1", "type": "n8n-nodes-base.filter", "position": [-940, 4640], "parameters": {"options": {}, "conditions": {"options": {"leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "461a5a59-f894-4dda-9233-175a1e228d23", "operator": {"type": "boolean", "operation": "true", "singleValue": true}, "leftValue": "={{ $json.property_sync_to_webflow }}", "rightValue": ""}]}}, "typeVersion": 2}, {"id": "2a9fab27-612e-4eb9-935c-fd802f39c96e", "name": "For each blog post1", "type": "n8n-nodes-base.splitInBatches", "position": [-360, 4640], "parameters": {"options": {}}, "typeVersion": 3}, {"id": "8f6d8e51-b92b-4780-b782-3f72203f40aa", "name": "Sticky Note17", "type": "n8n-nodes-base.stickyNote", "position": [540, 4720], "parameters": {"width": 777.880012347261, "height": 287.94399632670337, "content": "### \u2699\ufe0f Turn blocks into rich text\nThis is where the magic happens \u2014 Notion blocks are mapped and turned into their respective html version. Works with all the major rich text elements: headings 1, headings 2, headings 3, normal, bold and italic text, quotes, bulleted lists, numbered lists and images with captions."}, "typeVersion": 1}, {"id": "9592c56d-9bb2-433e-b49c-ec634e3d1db2", "name": "Sticky Note18", "type": "n8n-nodes-base.stickyNote", "position": [1980, 4420], "parameters": {"width": 218.00983675699544, "height": 394.8629861599825, "content": "### \u2705 Create a new post or update an existing one?\nThis node compares (by slug) your Notion post with all your Webflow posts and chooses whether to create a new one (in \"A only\" branch) or update an existing one (in \"different\" branch)."}, "typeVersion": 1}, {"id": "3ffb06d2-c1f1-4ce1-961f-8ece894d6cca", "name": "Create post1", "type": "n8n-nodes-base.webflow", "position": [2400, 4460], "parameters": {"siteId": "65a40576635069142ed11d7c", "fieldsUi": {"fieldValues": [{"fieldId": "name", "fieldValue": "={{ $json[\"name\"] }}"}, {"fieldId": "slug", "fieldValue": "={{ $json.property_slug }}"}, {"fieldId": "blog-post-richt-text", "fieldValue": "={{ $json.newRichText }}"}, {"fieldId": "_archived", "fieldValue": "false"}, {"fieldId": "_draft", "fieldValue": "true"}, {"fieldId": "blog-post-featured-image-photo", "fieldValue": "={{ $json.cover_url }}"}, {"fieldId": "blog-post-thumbnail-image-photo", "fieldValue": "={{ $json.cover_url }}"}]}, "operation": "create", "collectionId": "65a40577635069142ed11dd8", "authentication": "oAuth2"}, "credentials": {"webflowOAuth2Api": {"id": "cGhEXKKL99szTUa1", "name": "Webflow account"}}, "retryOnFail": true, "typeVersion": 1}, {"id": "e6490f39-b420-488c-b407-948425615764", "name": "Sticky Note19", "type": "n8n-nodes-base.stickyNote", "position": [-140, 3960], "parameters": {"width": 233.87813121439967, "height": 389.3234455133497, "content": "### \ud83c\udf89 Success\nSend a success message where you want.\n\nYou can remove this node.\n\nNote: If you're on it, you may need to refresh the Webflow page."}, "typeVersion": 1}, {"id": "13568b0a-9665-4149-b848-2dc355b91126", "name": "Update slug on posts1", "type": "n8n-nodes-base.notion", "position": [2920, 4760], "parameters": {"pageId": {"__rl": true, "mode": "id", "value": "={{ $('Compare by slug1').item.json.different.id.inputA }}"}, "options": {}, "resource": "databasePage", "operation": "update", "propertiesUi": {"propertyValues": [{"key": "slug|rich_text", "textContent": "={{ $json.slug }}"}]}}, "credentials": {"notionApi": {"id": "rxtaEXgFPg96muhy", "name": "My Notion account"}}, "retryOnFail": true, "typeVersion": 2.2}, {"id": "8574c1d2-491d-4bbb-bcc1-0bef64b321a2", "name": "Slug uniqueness checker and differentiator1", "type": "n8n-nodes-base.code", "notes": "Add a number to the slug if it's not unique", "position": [-660, 4640], "parameters": {"jsCode": "const data = $input.all().map(item => item.json)\nconst slugCount = {};\n\nreturn data.map(item => {\n  let slug = item.property_slug;\n  \n  if (slugCount[slug]) {\n    slugCount[slug] += 1;\n    slug = `${slug}-${slugCount[slug]}`;\n  } else {\n    slugCount[slug] = 1;\n  }\n  \n  item.property_slug = slug;\n  return item;\n});"}, "notesInFlow": true, "typeVersion": 2}, {"id": "21755856-9123-4acd-b343-3af878d665ad", "name": "Success message1", "type": "n8n-nodes-base.slack", "position": [-80, 4175], "parameters": {"text": "=[Notion to Webflow] \u2014 \"{{ $json.name }}\" successfully synced \ud83c\udf89", "select": "channel", "channelId": {"__rl": true, "mode": "list", "value": "C07719A0GF5", "cachedResultName": "general"}, "otherOptions": {}, "authentication": "oAuth2"}, "credentials": {"slackOAuth2Api": {"id": "qY28oJXU3BH6OrP3", "name": "Desengineers Account"}}, "typeVersion": 2.2}, {"id": "6c232d4a-464b-4d5a-992b-f649d955eb1e", "name": "Merge2", "type": "n8n-nodes-base.merge", "position": [2660, 4540], "parameters": {"mode": "combine", "options": {}, "combinationMode": "mergeByPosition"}, "typeVersion": 2.1}, {"id": "6af0cab5-8f70-435f-a341-c22d157d9200", "name": "Compare by slug1", "type": "n8n-nodes-base.compareDatasets", "position": [2040, 4640], "parameters": {"options": {}, "mergeByFields": {"values": [{"field1": "property_slug", "field2": "slug"}]}}, "typeVersion": 2.3}, {"id": "54a7dcf6-188e-4ca5-bc1e-3e76d5536236", "name": "Add slug to posts1", "type": "n8n-nodes-base.notion", "position": [2900, 4540], "parameters": {"pageId": {"__rl": true, "mode": "id", "value": "={{ $json.id }}"}, "options": {}, "resource": "databasePage", "operation": "update", "propertiesUi": {"propertyValues": [{"key": "slug|rich_text", "textContent": "={{ $json.slug }}"}]}}, "credentials": {"notionApi": {"id": "rxtaEXgFPg96muhy", "name": "My Notion account"}}, "retryOnFail": true, "typeVersion": 2.2}, {"id": "f9a66b20-ce82-4f36-b145-283dadf97d34", "name": "Get all collection posts1", "type": "n8n-nodes-base.webflow", "position": [1720, 4780], "parameters": {"siteId": "65a40576635069142ed11d7c", "operation": "getAll", "returnAll": true, "collectionId": "65a40577635069142ed11dd8", "authentication": "oAuth2"}, "credentials": {"webflowOAuth2Api": {"id": "cGhEXKKL99szTUa1", "name": "Webflow account"}}, "typeVersion": 1}, {"id": "c09f3782-12a1-4a91-945d-cd1ed14bfeb3", "name": "Data transporter, Notion posts to sync1", "type": "n8n-nodes-base.noOp", "position": [1720, 4480], "parameters": {}, "typeVersion": 1}, {"id": "9dc3ee15-4b4c-463c-a3b5-17b1dcb275da", "name": "Craft the rich text element1", "type": "n8n-nodes-base.code", "position": [1160, 4836], "parameters": {"jsCode": "const blocks = $input.all().map(item => item.json);\n\nlet newRichText = '';\nlet bulletedListItems = [];\nlet numberedListItems = [];\n\nblocks.forEach(block => {\n  if (block.type === 'bulleted_list_item') {\n    bulletedListItems.push(block.html);\n  } else if (block.type === 'numbered_list_item') {\n    numberedListItems.push(block.html);\n  } else {\n    if (bulletedListItems.length > 0) {\n      newRichText += `<ul>${bulletedListItems.join('')}</ul>`;\n      bulletedListItems = [];\n    }\n    if (numberedListItems.length > 0) {\n      newRichText += `<ol>${numberedListItems.join('')}</ol>`;\n      numberedListItems = [];\n    }\n    newRichText += block.html;\n  }\n});\n\nif (bulletedListItems.length > 0) {\n  newRichText += `<ul>${bulletedListItems.join('')}</ul>`;\n}\nif (numberedListItems.length > 0) {\n  newRichText += `<ol>${numberedListItems.join('')}</ol>`;\n}\n\nconst output = [{ newRichText }];\nreturn output;\n\n"}, "typeVersion": 2}, {"id": "e4ca0e5a-21bb-4d38-8448-8195b8994c12", "name": "Turn blocks into HTML1", "type": "n8n-nodes-base.code", "position": [860, 4840], "parameters": {"jsCode": "const blocks = $input.all().map(item => item.json);\nconst output = [];\n\nblocks.forEach(block => {\n  let html = '';\n  \n  switch (block.type) {\n    case 'heading_1':\n      html = block.heading_1.text.map(item => item.text.content).join(' ');\n      html = `<h1>${html}</h1>`;\n      break;\n    case 'heading_2':\n      html = block.heading_2.text.map(item => item.text.content).join(' ');\n      html = `<h2>${html}</h2>`;\n      break;\n    case 'heading_3':\n      html = block.heading_3.text.map(item => item.text.content).join(' ');\n      html = `<h3>${html}</h3>`;\n      break;\n    case 'paragraph':\n      html = `<p>${block.paragraph.text.map(item => {\n        let content = item.text.content.trim();\n        if (item.annotations.bold) content = `<b>${content}</b>`;\n        if (item.annotations.italic) content = `<i>${content}</i>`;\n        if (item.text.link) content = `<a href=\"${item.text.link.url}\">${content}</a>`;\n        return content;\n      }).join(' ') || '   '}</p>`; // the space inside the apostrophes is on purpose, otherwise Webflow will automatically delete the empty blocks\n      break;\n    case 'quote':\n      html = block.quote.text.map(item => item.text.content).join(' ');\n      html = `<blockquote>${html}</blockquote>`;\n      break;\n    case 'bulleted_list_item':\n      html = block.bulleted_list_item.text.map(item => item.text.content).join(' ');\n      html = `<li>${html}</li>`;\n      break;\n    case 'numbered_list_item':\n      html = block.numbered_list_item.text.map(item => item.text.content).join(' ');\n      html = `<li>${html}</li>`;\n      break;\n    case 'image':\n      const caption = block.image.caption.map(item => item.text.content).join(' ');\n      html = `<figure><img src=\"${block.image.file.url}\" alt=\"${caption}\" /><figcaption>${caption}</figcaption></figure>`;\n      break;\n    case 'code':\n      const codeContent = block.code.text.map(item => item.text.content).join('\\n')\n      html = `<pre><code>${codeContent}</code></pre>`\n      break\n    default:\n      html = block.content ? `<div>${block.content}</div>` : '';\n  }\n\n  if (html) {\n    output.push({\n      block_id: block.id,\n      type: block.type,\n      html\n    });\n  }\n});\n\nreturn output;\n"}, "typeVersion": 2}, {"id": "719f5116-5e60-488c-81c2-d55cea2e2646", "name": "Get blocks1", "type": "n8n-nodes-base.notion", "position": [580, 4837], "parameters": {"blockId": {"__rl": true, "mode": "id", "value": "={{ $json.id }}"}, "resource": "block", "operation": "getAll", "returnAll": true, "simplifyOutput": false}, "credentials": {"notionApi": {"id": "rxtaEXgFPg96muhy", "name": "My Notion account"}}, "typeVersion": 2.2}, {"id": "23f88f9c-ef4a-4158-bff5-728e2cf0383a", "name": "Update in \"Blog Posts\"", "type": "n8n-nodes-base.webflow", "maxTries": 3, "position": [2660, 4780], "parameters": {"itemId": "={{ $json.webflow_item_id }}", "siteId": "65a40576635069142ed11d7c", "fieldsUi": {"fieldValues": [{"fieldId": "_draft", "fieldValue": "true"}, {"fieldId": "_archived", "fieldValue": "false"}, {"fieldId": "name", "fieldValue": "={{ $json.name }}"}, {"fieldId": "slug", "fieldValue": "={{ $json.property_slug }}"}, {"fieldId": "blog-post-richt-text", "fieldValue": "={{ $json.newRichText }}"}, {"fieldId": "blog-post-featured-image-photo", "fieldValue": "={{ $json.cover_url }}"}, {"fieldId": "blog-post-thumbnail-image-photo", "fieldValue": "={{ $json.cover_url }}"}]}, "operation": "update", "collectionId": "65a40577635069142ed11dd8", "authentication": "oAuth2"}, "credentials": {"webflowOAuth2Api": {"id": "cGhEXKKL99szTUa1", "name": "Webflow account"}}, "retryOnFail": true, "typeVersion": 1, "alwaysOutputData": false}, {"id": "6db40a4d-4acd-40f3-8830-f17e00678e39", "name": "Add Webflow item id to Notion data", "type": "n8n-nodes-base.code", "position": [2400, 4760], "parameters": {"mode": "runOnceForEachItem", "jsCode": "const compareResult = $json\nconst notionData = $('Final Notion post data').item.json\n\nconst output = {\n  ...notionData, // spread notion data\n  webflow_item_id: compareResult.different._id.inputB // add the webflow item id\n}\n\nreturn output"}, "typeVersion": 2}, {"id": "49e3d52c-a95a-4ac0-ae6a-69e4a722a628", "name": "Final Notion post data", "type": "n8n-nodes-base.merge", "position": [1380, 4640], "parameters": {"mode": "combine", "options": {}, "combinationMode": "mergeByPosition"}, "typeVersion": 2.1}, {"id": "23755e8c-0012-4a72-ad9e-f450ceca1de4", "name": "Sticky Note1", "type": "n8n-nodes-base.stickyNote", "position": [-146, 4720], "parameters": {"width": 366.7438380520149, "height": 282.04364735085795, "content": "### No wastes\nThese nodes extract the cover image url of the Notion page to make it easy for you to use it in the collection fields."}, "typeVersion": 1}, {"id": "cb16a61b-73bc-491b-b4ce-b4dc5a5f21fc", "name": "Schedule Trigger", "type": "n8n-nodes-base.scheduleTrigger", "position": [-1480, 4640], "parameters": {"rule": {"interval": [{}]}}, "typeVersion": 1.2}], "connections": {"Merge1": {"main": [[{"node": "Final Notion post data", "type": "main", "index": 0}, {"node": "Get blocks1", "type": "main", "index": 0}]]}, "Merge2": {"main": [[{"node": "Add slug to posts1", "type": "main", "index": 0}]]}, "Get blocks1": {"main": [[{"node": "Turn blocks into HTML1", "type": "main", "index": 0}]]}, "Create post1": {"main": [[{"node": "Merge2", "type": "main", "index": 0}]]}, "Take cover url": {"main": [[{"node": "Merge1", "type": "main", "index": 1}]]}, "Compare by slug1": {"main": [[{"node": "Create post1", "type": "main", "index": 0}, {"node": "Merge2", "type": "main", "index": 1}], null, [{"node": "Add Webflow item id to Notion data", "type": "main", "index": 0}]]}, "Schedule Trigger": {"main": [[{"node": "Get all blog posts1", "type": "main", "index": 0}]]}, "Data transporter1": {"main": [[{"node": "For each blog post1", "type": "main", "index": 0}]]}, "Get all page data": {"main": [[{"node": "Take cover url", "type": "main", "index": 0}]]}, "Is sync checked?1": {"main": [[{"node": "Slug uniqueness checker and differentiator1", "type": "main", "index": 0}]]}, "Add slug to posts1": {"main": [[{"node": "Data transporter1", "type": "main", "index": 0}]]}, "For each blog post1": {"main": [[{"node": "Success message1", "type": "main", "index": 0}], [{"node": "Get simple page data", "type": "main", "index": 0}, {"node": "Get all page data", "type": "main", "index": 0}]]}, "Get all blog posts1": {"main": [[{"node": "Is sync checked?1", "type": "main", "index": 0}]]}, "Get simple page data": {"main": [[{"node": "Merge1", "type": "main", "index": 0}]]}, "Update slug on posts1": {"main": [[{"node": "Data transporter1", "type": "main", "index": 0}]]}, "Final Notion post data": {"main": [[{"node": "Data transporter, Notion posts to sync1", "type": "main", "index": 0}, {"node": "Get all collection posts1", "type": "main", "index": 0}]]}, "Turn blocks into HTML1": {"main": [[{"node": "Craft the rich text element1", "type": "main", "index": 0}]]}, "Update in \"Blog Posts\"": {"main": [[{"node": "Update slug on posts1", "type": "main", "index": 0}]]}, "Get all collection posts1": {"main": [[{"node": "Compare by slug1", "type": "main", "index": 1}]]}, "Craft the rich text element1": {"main": [[{"node": "Final Notion post data", "type": "main", "index": 1}]]}, "Add Webflow item id to Notion data": {"main": [[{"node": "Update in \"Blog Posts\"", "type": "main", "index": 0}]]}, "Data transporter, Notion posts to sync1": {"main": [[{"node": "Compare by slug1", "type": "main", "index": 0}]]}, "Slug uniqueness checker and differentiator1": {"main": [[{"node": "For each blog post1", "type": "main", "index": 0}]]}}}