This workflow corresponds to n8n.io template #9917 — we link there as the canonical source.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"nodes": [
{
"id": "863c7ba9-dcbe-4cbb-a451-aff131934ef1",
"name": "Get a database page",
"type": "n8n-nodes-base.notion",
"position": [
-448,
208
],
"parameters": {
"pageId": {
"__rl": true,
"mode": "url",
"value": "={{ $json.notion_url }}"
},
"resource": "databasePage",
"operation": "get"
},
"credentials": {
"notionApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "80ccc7d0-f503-48c3-bf8f-5c98edcbe543",
"name": "Get many child blocks",
"type": "n8n-nodes-base.notion",
"position": [
-224,
208
],
"parameters": {
"blockId": {
"__rl": true,
"mode": "url",
"value": "={{ $json.url }}"
},
"resource": "block",
"operation": "getAll",
"returnAll": true,
"simplifyOutput": false,
"fetchNestedBlocks": true
},
"credentials": {
"notionApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "b62bd11d-eafb-49f5-af62-948ea9dea525",
"name": "decode blocks",
"type": "n8n-nodes-base.code",
"position": [
224,
208
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const c = $json.content\n\nswitch ($json.type) {\n case 'code':\n $json.content = $json.code.text[0].plain_text\n break;\n case 'video':\n if ($json.video.external.url.match('vimeo'))\n // $json.content = `[vimeo src=\"${$json.video.external.url}\"]`\n $json.content = `<!-- wp:shortcode -->[embed]${$json.video.external.url}[/embed]\n<!-- /wp:shortcode -->`\n else if ($json.video.external.url.match('youtu'))\n $json.content = `<!-- wp:shortcode -->\n[embed]${$json.video.external.url}[/embed]\n<!-- /wp:shortcode -->`\n break\n case 'heading_3':\n $json.content = `<!-- wp:heading {\"level\":3} -->\n<h3>${$json.heading_3.text[0].plain_text}</h3>\n<!-- /wp:heading -->`\n break\n case 'heading_2':\n $json.content = `<!-- wp:heading {\"level\":2} -->\n<h2>${$json.heading_2.text[0].plain_text}</h2>\n<!-- /wp:heading -->`\n break\n case 'heading_1':\n $json.content = `<!-- wp:heading {\"level\":2} -->\n<h1>${$json.heading_1.text[0].plain_text}</h1>\n<!-- /wp:heading -->`\n break\n case 'paragraph':\n $json.content = `\n<!-- wp:paragraph -->\n<p>${c}</p>\n<!-- /wp:paragraph -->`\n break\n case 'numbered_list_item':\n $json.content = `\n<!-- wp:list {\"ordered\": true} -->\n<ol class=\\\"wp-block-list\\\">\n<!-- wp:list-item -->\n<li>${c}</li>\n<!-- /wp:list-item -->\n</ol>\n<!-- /wp:list -->`\n break\n case 'bulleted_list_item':\n $json.content = `<!-- wp:list -->\n<ul class=\\\"wp-block-list\\\">\n<!-- wp:list-item -->\n<li>${c}</li>\n<!-- /wp:list-item -->\n</ul>\n<!-- /wp:list -->`\n break\n case 'image':\n $json.content = `<figure class=\"wp-block-image\">\n<img src=\"${$json?.image?.external?.url || $json?.image?.file?.url}\" alt=\"\"/>\n</figure>`\n break\n case 'audio':\n $json.content = `<!-- wp:audio -->\n<figure class=\"wp-block-audio\">\n <audio controls src=\"${$json?.audio?.external?.url || $json?.audio?.file?.url}\"></audio>\n</figure>\n<!-- /wp:audio -->`\n break\n case 'embed':\n $json.content = ` <!-- wp:embed {\"url\":\"${$json.embed.url}\"} /-->`\n break\n case 'divider':\n $json.content = `<hr class=\"wp-block-separator\"/>`\n break\n default:\n $json.content = `<!-- wp:html -->\n${$json.content}\n<!-- /wp:html -->`\n break\n}\n\n\nreturn $json"
},
"typeVersion": 2
},
{
"id": "11fb7b82-a913-4792-a33b-dff0751512af",
"name": "decode paragraphs",
"type": "n8n-nodes-base.code",
"position": [
0,
208
],
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const annotateText = (t) => {\n let v = t?.text?.content || t.plain_text\n const a = t.annotations\n v = a.bold ? `<strong>${v}</strong>` : v\n v = a.italic ? `<em>${v}</em>` : v\n v = a.strikethrough ? '<s>${v}</s>' : v\n v = a.underline ? '<u>${v}</u>' : v\n if (t?.text?.link) {\n v = `<a href=\"${t.text.link.url}\">${v}</a>`\n }\n return v\n}\n\nswitch ($json.type) {\n case 'paragraph':\n case 'numbered_list_item':\n case 'bulleted_list_item':\n $json.content = $json[$json.type]\n .text\n .map(annotateText)\n .join(' ')\n break\n}\n\nreturn $json"
},
"typeVersion": 2
},
{
"id": "bd2edec6-ffc7-4e80-884d-4fbc9ecb30a7",
"name": "Aggregate",
"type": "n8n-nodes-base.aggregate",
"position": [
1344,
208
],
"parameters": {
"options": {
"mergeLists": true
},
"fieldsToAggregate": {
"fieldToAggregate": [
{
"fieldToAggregate": "wp"
}
]
}
},
"typeVersion": 1
},
{
"id": "1d83b6b5-9bbe-4a8d-8a79-82ad98b7a28a",
"name": "When Executed by Another Workflow",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"position": [
-672,
304
],
"parameters": {
"workflowInputs": {
"values": [
{
"name": "notion_url"
}
]
}
},
"typeVersion": 1.1
},
{
"id": "1754c892-4aae-46f2-a90c-40fff5d9c88e",
"name": "When clicking \u2018Execute workflow\u2019",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-896,
112
],
"parameters": {},
"typeVersion": 1
},
{
"id": "03f0e6ad-8a8b-40c0-a392-4441274b61f0",
"name": "Edit Fields2",
"type": "n8n-nodes-base.set",
"position": [
-672,
112
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "bb704bf6-9a59-49f0-872b-56469fa52263",
"name": "notion_url",
"type": "string",
"value": ""
}
]
}
},
"typeVersion": 3.4
},
{
"id": "ef9e41a2-38b9-4a9e-87f6-bb19aade231f",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-496,
-176
],
"parameters": {
"color": 6,
"width": 416,
"height": 656,
"content": "## \ud83d\udce5 Extract: Fetch Notion Content\n\nThis stage retrieves all the content from the provided Notion page URL.\n\n**Get a database page:** Fetches the main page object. This is needed to get the page's canonical ID, which is used later for filtering.\n\n**Get many child blocks:** Fetches all blocks on the page. Crucially, the fetchNestedBlocks: true option is enabled, so it recursively gets all content, including blocks inside columns, toggles, etc."
},
"typeVersion": 1
},
{
"id": "2af6a568-782c-44ed-b04c-5356f67f1139",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-48,
-176
],
"parameters": {
"color": 5,
"width": 880,
"height": 656,
"content": "## \u2699\ufe0f Transform: Convert Notion to Gutenberg HTML\n\nThis is the core logic of the workflow. It iterates over every block (in multiple passes) to build the final HTML.\n\n1. **decode paragraphs (Code):** Handles rich text. It loops through text-based blocks (paragraphs, lists) and converts Notion's annotations array (bold, italic, strikethrough, underline, and links) into the corresponding HTML tags (e.g., <strong>, <em>, <a href=\"...\">).\n\n2. **decode blocks (Code):** A large switch statement that maps each Notion block type (e.g., heading_1, image, video, divider) to its correct WordPress Gutenberg block structure (e.g., <figure class=\"wp-block-image\">..., <hr class=\"wp-block-separator\"/>).\n\n3. **drop unnecessary fields (Set):** Cleans up the data after conversion, keeping only the fields needed for the next step (id, parent_id, type, and the new content field).\n\n4. **nested blocks (Code):** This is a critical step for handling layouts. It runs once on all blocks to correctly rebuild column_list and column blocks. It finds all child blocks of each column and injects their HTML inside the parent column_list block, wrapping them in the correct and tags."
},
"typeVersion": 1
},
{
"id": "18124fd4-c9f7-4ebc-9353-d5fde739cca3",
"name": "drop unnecessary fields",
"type": "n8n-nodes-base.set",
"position": [
448,
208
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "0630be0c-738a-4f1b-b782-67de592305dc",
"name": "id",
"type": "string",
"value": "={{ $json.id }}"
},
{
"id": "71be8325-4dc1-4567-972a-74ee46c7fe50",
"name": "parent_id",
"type": "string",
"value": "={{ $json.parent_id }}"
},
{
"id": "f1629c55-71fa-4c4f-bb94-c09714827475",
"name": "type",
"type": "string",
"value": "={{ $json.type }}"
},
{
"id": "32db7ae9-9b52-46d8-a75d-61d6e2ba9594",
"name": "content",
"type": "string",
"value": "={{ $json.content }}"
},
{
"id": "efcfcaa9-4e72-4f57-8740-c89a68eceb0c",
"name": "has_children",
"type": "boolean",
"value": "={{ $json.has_children }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "fd03cfb2-3fc1-4dff-ba44-d40d64cb5f5c",
"name": "nested blocks",
"type": "n8n-nodes-base.code",
"position": [
672,
208
],
"parameters": {
"jsCode": "const all = $input.all()\n\nfor (const item of all) {\n if (item.json.type !== 'column') continue\n\n item.json.children = all\n .filter(_ => _.json.parent_id == item.json.id)\n .map(_ => _.json.content)\n .join(\"\\n\")\n item.json.children = \"<!-- wp:column -->\\n<div class=\\\"wp-block-column\\\">\\n\" + item.json.children + \"\\n</div>\\n<!-- /wp:column -->\\n\"\n}\n\nfor (const item of all) {\n if (item.json.type !== 'column_list') continue\n\n item.json.children = all\n .filter(_ => _.json.parent_id == item.json.id)\n .map(_ => _.json.children)\n .join(\"\\n\")\n item.json.children = \"<!-- wp:columns -->\\n<div class=\\\"wp-block-columns\\\">\\n\" + item.json.children + \"\\n</div>\\n<!-- /wp:columns -->\\n\"\n}\n\nreturn all"
},
"typeVersion": 2
},
{
"id": "774f0093-478f-4eb9-979d-0068dba63ff3",
"name": "choose content field",
"type": "n8n-nodes-base.set",
"position": [
1120,
208
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "bb93822c-5868-49a4-abbe-d72d8868fc47",
"name": "wp",
"type": "string",
"value": "={{ $json.children || $json.content }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "bbdfd21e-df44-4ad8-b57a-08f6691c8536",
"name": "only top level blocks",
"type": "n8n-nodes-base.filter",
"position": [
896,
208
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "2de1606e-f16f-4455-9c8e-8ccb54ce197c",
"operator": {
"name": "filter.operator.equals",
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.parent_id.replace(/-/g, '') }}",
"rightValue": "={{ $('Get a database page').item.json.id.replace(/-/g,'') }}"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "43a66246-0dfb-445d-b55f-da2c6d8be17a",
"name": "join lines",
"type": "n8n-nodes-base.set",
"position": [
1568,
208
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "b995ade4-ee5c-4d7a-89a5-f01b039f7e62",
"name": "wp",
"type": "string",
"value": "={{ $json.wp.join(\"\\n\") }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "8c7a766d-5499-47d5-abbf-451488ea24be",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
848,
-176
],
"parameters": {
"color": 4,
"width": 928,
"height": 656,
"content": "## \ud83e\udde9 Finalize: Assemble the Post Body\n\nThis stage takes all the individual HTML block strings and assembles them into a single, complete HTML document ready for WordPress.\n\n**only top level blocks (Filter):** Filters the list to only include blocks that are direct children of the page (i.e., their parent_id matches the main page ID). This is essential because all nested blocks (like those in columns) have already been moved inside their parent block's content in the previous step.\n\n**choose content field (Set):** Creates a single, consistently-named field wp that holds the final HTML for each block. It uses ($json.children || $json.content) to ensure it gets the content from nested blocks (like columns) or regular blocks.\n\n**Aggregate:** Merges all the separate items (each with its wp HTML string) into a single item containing an array of all the HTML strings.\n\n**join lines (Set):** Joins the array of HTML strings together, separated by newlines (\\n), into one final text string. This string is in the wp field and is the final output of the sub-workflow."
},
"typeVersion": 1
},
{
"id": "31568ee0-614f-4563-b831-f389e0dc0cef",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1568,
80
],
"parameters": {
"width": 1040,
"height": 400,
"content": "## \ud83d\ude80 Triggers (How to Run)\n\nThis workflow is designed as a sub-workflow.\n\n### Manual Test:\n\nClick \"Execute workflow\".\n\nYou must paste a valid Notion page URL into the Edit Fields2 node first.\n\n### Production (Sub-workflow):\n\nTrigger this from a parent workflow using the When Executed by Another Workflow trigger.\n\nYour parent workflow must pass a JSON object containing a notion_url key."
},
"typeVersion": 1
}
],
"connections": {
"Aggregate": {
"main": [
[
{
"node": "join lines",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields2": {
"main": [
[
{
"node": "Get a database page",
"type": "main",
"index": 0
}
]
]
},
"decode blocks": {
"main": [
[
{
"node": "drop unnecessary fields",
"type": "main",
"index": 0
}
]
]
},
"nested blocks": {
"main": [
[
{
"node": "only top level blocks",
"type": "main",
"index": 0
}
]
]
},
"decode paragraphs": {
"main": [
[
{
"node": "decode blocks",
"type": "main",
"index": 0
}
]
]
},
"Get a database page": {
"main": [
[
{
"node": "Get many child blocks",
"type": "main",
"index": 0
}
]
]
},
"choose content field": {
"main": [
[
{
"node": "Aggregate",
"type": "main",
"index": 0
}
]
]
},
"Get many child blocks": {
"main": [
[
{
"node": "decode paragraphs",
"type": "main",
"index": 0
}
]
]
},
"only top level blocks": {
"main": [
[
{
"node": "choose content field",
"type": "main",
"index": 0
}
]
]
},
"drop unnecessary fields": {
"main": [
[
{
"node": "nested blocks",
"type": "main",
"index": 0
}
]
]
},
"When Executed by Another Workflow": {
"main": [
[
{
"node": "Get a database page",
"type": "main",
"index": 0
}
]
]
},
"When clicking \u2018Execute workflow\u2019": {
"main": [
[
{
"node": "Edit Fields2",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
notionApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow fetches the complete content of a specific Notion page and converts all its blocks into a single HTML string compatible with the WordPress Gutenberg block editor.
Source: https://n8n.io/workflows/9917/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This template can backup WordPress context github。
This workflow generates high-quality AI images from text prompts using Leonardo AI, then automatically uploads the result to your WordPress media library and returns the final image URL.
Enrich Faq Sections On Your Website Pages At Scale With Ai. Uses manualTrigger, lmChatOpenAi, splitInBatches, splitOut. Event-driven trigger; 36 nodes.
Strapi Splitout. Uses manualTrigger, lmChatOpenAi, splitInBatches, splitOut. Event-driven trigger; 36 nodes.
2434. Uses lmChatOpenAi, googleDrive, googleSheets, executeWorkflowTrigger. Event-driven trigger; 36 nodes.