This workflow follows the Agent → HTTP Request recipe pattern — see all workflows that pair these two integrations.
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": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "weeks",
"triggerAtDay": [
1,
4
],
"triggerAtHour": 8
}
]
}
},
"id": "a2ab0403-cf77-4f0f-a20b-67460c6e795a",
"name": "\u23f0 Schedule (Lun/Jue 8am)",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
864,
480
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "a2",
"name": "devto_api_key",
"value": "{{ DEVTO_API_KEY }}",
"type": "string"
},
{
"id": "a3",
"name": "supabase_service_key",
"value": "{{ SUPABASE_SERVICE_ROLE_KEY }}",
"type": "string"
},
{
"id": "a6",
"name": "github_owner",
"value": "Mgobeaalcoba",
"type": "string"
},
{
"id": "a7",
"name": "github_repo",
"value": "Mgobeaalcoba.github.io",
"type": "string"
}
]
},
"options": {}
},
"id": "2a4f3214-8c2e-4e12-a1f2-1289f1af9d1f",
"name": "\u2699\ufe0f Configuraci\u00f3n",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1088,
480
]
},
{
"parameters": {
"jsCode": "\nconst xml = $input.item.json.data || $input.item.json;\nconst xmlStr = typeof xml === 'string' ? xml : JSON.stringify(xml);\n\n// Extract <item> blocks\nconst items = [];\nconst itemRegex = /<item[^>]*>([\\s\\S]*?)<\\/item>/gi;\nlet match;\nwhile ((match = itemRegex.exec(xmlStr)) !== null && items.length < 6) {\n const block = match[1];\n const get = (tag) => {\n const m = block.match(new RegExp('<' + tag + '[^>]*><!\\\\[CDATA\\\\[([\\\\s\\\\S]*?)\\\\]\\\\]><\\/' + tag + '>|<' + tag + '[^>]*>([^<]*)<\\/' + tag + '>', 'i'));\n return m ? (m[1] || m[2] || '').trim() : '';\n };\n items.push({\n title: get('title'),\n description: get('description'),\n link: get('link'),\n pubDate: get('pubDate')\n });\n}\n\n// Filter relevant to data/AI/engineering\nconst keywords = ['ai', 'machine learning', 'data', 'model', 'llm', 'gemini', 'python', 'cloud', 'automation', 'engineering'];\nconst scored = items.map(item => {\n const text = (item.title + ' ' + item.description).toLowerCase();\n const score = keywords.reduce((s, kw) => s + (text.includes(kw) ? 1 : 0), 0);\n return { ...item, score };\n}).sort((a, b) => b.score - a.score);\n\nconst selected = scored[0];\nif (!selected || !selected.title) throw new Error('No relevant RSS items found');\n\nreturn {\n topic_title: selected.title,\n topic_description: selected.description,\n topic_link: selected.link,\n topic_date: selected.pubDate,\n source: 'Hacker News'\n};\n"
},
"id": "de503336-c295-464a-8479-cacb9b9fe444",
"name": "\ud83d\udd27 Parsear RSS",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1536,
480
]
},
{
"parameters": {
"jsCode": "const config = $('\u2699\ufe0f Configuraci\u00f3n').item.json;\nconst topic = $('\ud83d\udd27 Parsear RSS').item.json;\nconst art = $input.item.json; // ya viene parseado\n\nconst today = new Date().toISOString().split('T')[0];\nconst filename = `${today}-${art.output.slug}.md`;\n\nconst fileContent = `---\\nslug: ${art.slug}\\ndate: ${today}\\n---\\n\\n${art.content_markdown_es}\\n`;\n\nreturn {\n ...art,\n filename,\n date: today,\n github_file_path: `cv/content/posts/${filename}`,\n github_file_content_base64: Buffer.from(fileContent, 'utf8').toString('base64'),\n canonical_url: `https://mgobeaalcoba.github.io/blog/${art.output.slug}/`,\n github_pat: config.github_pat,\n devto_api_key: config.devto_api_key,\n supabase_service_key: config.supabase_service_key\n};"
},
"id": "b1d8d91f-0dc4-4631-9fbd-dc36d4a0c857",
"name": "\ud83d\udd27 Parsear Art\u00edculo",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2256,
480
]
},
{
"parameters": {
"method": "POST",
"url": "{{ SUPABASE_URL }}/rest/v1/blog_posts",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "={{ $('\ud83d\udd27 Parsear Art\u00edculo').item.json.supabase_service_key }}"
},
{
"name": "Authorization",
"value": "=Bearer {{ $('\ud83d\udd27 Parsear Art\u00edculo').item.json.supabase_service_key }}"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "Prefer",
"value": "return=representation"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"slug\": \"{{ $('\ud83d\udd27 Parsear Art\u00edculo').item.json.output.slug }}\",\n \"file\": \"{{ $('\ud83d\udd27 Parsear Art\u00edculo').item.json.filename }}\",\n \"title_es\": {{ JSON.stringify($('\ud83d\udd27 Parsear Art\u00edculo').item.json.output.title_es) }},\n \"title_en\": {{ JSON.stringify($('\ud83d\udd27 Parsear Art\u00edculo').item.json.output.title_en) }},\n \"excerpt_es\": {{ JSON.stringify($('\ud83d\udd27 Parsear Art\u00edculo').item.json.output.excerpt_es) }},\n \"excerpt_en\": {{ JSON.stringify($('\ud83d\udd27 Parsear Art\u00edculo').item.json.output.excerpt_en) }},\n \"date\": \"{{ $('\ud83d\udd27 Parsear Art\u00edculo').item.json.date }}\",\n \"category\": \"{{ $('\ud83d\udd27 Parsear Art\u00edculo').item.json.output.category }}\",\n \"featured\": false,\n \"read_time\": \"{{ $('\ud83d\udd27 Parsear Art\u00edculo').item.json.output.read_time }}\",\n \"author\": \"Mariano Gobea Alcoba\",\n \"sort_order\": 99\n}",
"options": {}
},
"id": "78caa053-7b8f-4139-86d5-24af872f5a78",
"name": "\ud83d\udcca Supabase: Insert Post",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2480,
480
]
},
{
"parameters": {
"jsCode": "\nconst art = $('\ud83d\udd27 Parsear Art\u00edculo').item.json.output;\nconst supabaseResult = $input.item.json;\n\n// supabaseResult is the newly created blog_post row\nconst newPost = Array.isArray(supabaseResult) ? supabaseResult[0] : supabaseResult;\nconst postId = newPost?.id;\nif (!postId) throw new Error('Supabase insert did not return an ID. Response: ' + JSON.stringify(supabaseResult));\n\n// Prepare tags for batch insert\nconst tagsPayload = art.tags.map(tag => ({\n post_id: postId,\n tag: tag.toLowerCase().replace(/[^a-z0-9]/g, '')\n})).filter(t => t.tag.length > 0);\n\nreturn {\n post_id: postId,\n tags_payload: tagsPayload,\n tags_payload_json: JSON.stringify(tagsPayload),\n slug: art.slug,\n title_en: art.title_en,\n canonical_url: art.canonical_url,\n content_markdown_en: art.content_markdown_en,\n tags: art.tags,\n devto_api_key: art.devto_api_key,\n supabase_service_key: art.supabase_service_key\n};\n"
},
"id": "e8a419f2-9939-4da5-82e3-8c99253c6811",
"name": "\ud83d\udd27 Preparar Tags",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2704,
480
]
},
{
"parameters": {
"method": "POST",
"url": "{{ SUPABASE_URL }}/rest/v1/blog_post_tags",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "={{ $('\u2699\ufe0f Configuraci\u00f3n').item.json.supabase_service_key }}"
},
{
"name": "Authorization",
"value": "=Bearer {{ $('\u2699\ufe0f Configuraci\u00f3n').item.json.supabase_service_key }}"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "Prefer",
"value": "return=minimal"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ $json.tags_payload_json }}",
"options": {}
},
"id": "00c491f6-1ed2-4363-a889-d7f026fd0e64",
"name": "\ud83d\udcca Supabase: Insert Tags",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
2928,
480
]
},
{
"parameters": {
"method": "POST",
"url": "https://dev.to/api/articles",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "api-key",
"value": "={{ $('\ud83d\udd27 Parsear Art\u00edculo').item.json.devto_api_key }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"article\": {\n \"title\": {{ JSON.stringify($('\ud83d\udd27 Parsear Art\u00edculo').item.json.output.title_en + \"!\") }},\n \"body_markdown\": {{ JSON.stringify($('\ud83d\udd27 Parsear Art\u00edculo').item.json.output.content_markdown_en + \"\\n\\n---\\n*Originally published in Spanish at [mgobeaalcoba.github.io/blog/\" + $('\ud83d\udd27 Parsear Art\u00edculo').item.json.output.slug + \"/](\" + $('\ud83d\udd27 Parsear Art\u00edculo').item.json.canonical_url + \")*\") }},\n \"published\": true,\n \"canonical_url\": {{ JSON.stringify($('\ud83d\udd27 Parsear Art\u00edculo').item.json.canonical_url) }},\n \"tags\": {{ JSON.stringify($('\ud83d\udd27 Parsear Art\u00edculo').item.json.output.tags.map(t => t.toLowerCase().replace(/[^a-z0-9]/g, '')).slice(0,4)) }},\n \"series\": \"Data Engineering in the Trenches\"\n }\n}",
"options": {}
},
"id": "77367945-3914-4229-969b-594d4ac21285",
"name": "\ud83d\ude80 dev.to: Publicar",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
3376,
480
]
},
{
"parameters": {
"url": "https://hnrss.org/frontpage",
"options": {
"response": {
"response": {
"responseFormat": "text"
}
}
}
},
"id": "4311c735-a69c-4e00-b9cc-d82daa434feb",
"name": "Hacker News RSS",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1312,
480
]
},
{
"parameters": {
"promptType": "define",
"text": "={{ $json.topic_title }}\n{{ $json.topic_description }}\n{{ $json.topic_link }}\n{{ $json.topic_date }}\n{{ $json.source }}",
"hasOutputParser": true,
"options": {
"systemMessage": "You are an expert technical writer specializing in Data Engineering, AI, Python, and Big Data. Your audience is software developers and data professionals in Latin America and globally.\n\nYour task is to write a COMPLETE technical blog post based on a trending topic provided by the user (from Google AI Blog RSS feed).\n\nThe article must be original, not a translation or summary of the source \u2014 use the topic as INSPIRATION to write something practical and actionable for data engineers and developers.\n\nAlways return your response as a single valid JSON object with this EXACT structure \u2014 no markdown wrapper, no explanation, just the raw JSON:\n\n{\n \"slug\": \"url-friendly-english-slug-max-6-words\",\n \"title_es\": \"T\u00edtulo atractivo en espa\u00f1ol (max 70 caracteres)\",\n \"title_en\": \"Attractive English title (max 70 characters)\",\n \"excerpt_es\": \"Resumen atractivo en espa\u00f1ol de 150-180 caracteres que invite a leer el art\u00edculo completo\",\n \"excerpt_en\": \"Attractive English summary of 150-180 characters that invites reading the full article\",\n \"category\": \"one of exactly: data-engineering, machine-learning, ai-tools, python, bigquery\",\n \"tags\": [\"tag1\", \"tag2\", \"tag3\", \"tag4\"],\n \"read_time\": \"X min\",\n \"content_markdown_es\": \"FULL article in Spanish (1500-2500 words). Start directly with content paragraphs \u2014 no frontmatter, no title heading at the top. Use ## for sections, ### for subsections, code blocks with triple backticks and language tag. Include real-world examples. End with a paragraph inviting readers to visit https://mgobeaalcoba.github.io/consulting/ for consulting services.\",\n \"content_markdown_en\": \"FULL article in English (1500-2500 words). Same structure and depth as the Spanish version but naturally written in English for a global developer audience \u2014 not a literal translation.\"\n}\n\nRules:\n- slug: lowercase, hyphens only, no special characters, descriptive of the content\n- tags: lowercase, no spaces, no special characters (e.g. python, bigquery, llm, dataengineering)\n- category: must be exactly one of the five options listed\n- content_markdown fields: must be complete articles with real depth, not summaries or outlines\n- Return ONLY the JSON object \u2014 nothing before or after it"
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3.1,
"position": [
1792,
480
],
"id": "43965d7e-2db4-47c5-9a3f-ed7171fb4e07",
"name": "AI Agent"
},
{
"parameters": {
"model": "z-ai/glm-4.5-air:free",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
"typeVersion": 1,
"position": [
1760,
704
],
"id": "aad66f26-4317-401c-8bc8-018e093264ca",
"name": "OpenRouter Chat Model",
"credentials": {
"openRouterApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"schemaType": "manual",
"inputSchema": "{\n \"type\": \"object\",\n \"properties\": {\n \"slug\": {\n \"type\": \"string\",\n \"description\": \"URL-friendly English slug, max 6 words, lowercase, hyphens only\"\n },\n \"title_es\": {\n \"type\": \"string\",\n \"description\": \"T\u00edtulo atractivo en espa\u00f1ol, m\u00e1ximo 70 caracteres\"\n },\n \"title_en\": {\n \"type\": \"string\",\n \"description\": \"Attractive English title, maximum 70 characters\"\n },\n \"excerpt_es\": {\n \"type\": \"string\",\n \"description\": \"Resumen en espa\u00f1ol de 150-180 caracteres que invite a leer el art\u00edculo\"\n },\n \"excerpt_en\": {\n \"type\": \"string\",\n \"description\": \"English summary of 150-180 characters that invites reading the full article\"\n },\n \"category\": {\n \"type\": \"string\",\n \"enum\": [\"data-engineering\", \"machine-learning\", \"ai-tools\", \"python\", \"bigquery\"],\n \"description\": \"Article category, must be exactly one of the enum values\"\n },\n \"tags\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"minItems\": 2,\n \"maxItems\": 4,\n \"description\": \"Lowercase tags, no spaces, no special characters\"\n },\n \"read_time\": {\n \"type\": \"string\",\n \"description\": \"Estimated read time, e.g. '8 min'\"\n },\n \"content_markdown_es\": {\n \"type\": \"string\",\n \"description\": \"Full article in Spanish, 1500-2500 words, markdown format, no frontmatter\"\n },\n \"content_markdown_en\": {\n \"type\": \"string\",\n \"description\": \"Full article in English, 1500-2500 words, markdown format, no frontmatter\"\n }\n },\n \"required\": [\n \"slug\",\n \"title_es\",\n \"title_en\",\n \"excerpt_es\",\n \"excerpt_en\",\n \"category\",\n \"tags\",\n \"read_time\",\n \"content_markdown_es\",\n \"content_markdown_en\"\n ]\n}",
"autoFix": true
},
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"typeVersion": 1.3,
"position": [
1888,
704
],
"id": "6f67e7c0-0def-49b3-a087-78815bdbc31d",
"name": "Structured Output Parser"
},
{
"parameters": {
"model": "z-ai/glm-4.5-air:free",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
"typeVersion": 1,
"position": [
1968,
912
],
"id": "c530fa46-51eb-44bc-93f3-cba97ebdf483",
"name": "OpenRouter Chat Model1",
"credentials": {
"openRouterApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"authentication": "oAuth2",
"resource": "file",
"owner": {
"__rl": true,
"value": "={{ $('\u2699\ufe0f Configuraci\u00f3n').item.json.github_owner }}",
"mode": "name"
},
"repository": {
"__rl": true,
"value": "={{ $('\u2699\ufe0f Configuraci\u00f3n').item.json.github_repo }}",
"mode": "name"
},
"filePath": "={{ $('\ud83d\udd27 Parsear Art\u00edculo').item.json.github_file_path }}",
"fileContent": "={{ $('\ud83d\udd27 Parsear Art\u00edculo').item.json.output.content_markdown_es }}",
"commitMessage": "=Delete {{ $('\ud83d\udd27 Parsear Art\u00edculo').item.json.output.slug }} to cv/content/posts"
},
"type": "n8n-nodes-base.github",
"typeVersion": 1.1,
"position": [
3152,
480
],
"id": "d45dfa7b-4c5b-42ab-ab6c-dfec84cb57c7",
"name": "Create a file",
"credentials": {
"githubOAuth2Api": {
"name": "<your credential>"
}
}
}
],
"connections": {
"\u23f0 Schedule (Lun/Jue 8am)": {
"main": [
[
{
"node": "\u2699\ufe0f Configuraci\u00f3n",
"type": "main",
"index": 0
}
]
]
},
"\u2699\ufe0f Configuraci\u00f3n": {
"main": [
[
{
"node": "Hacker News RSS",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd27 Parsear RSS": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd27 Parsear Art\u00edculo": {
"main": [
[
{
"node": "\ud83d\udcca Supabase: Insert Post",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcca Supabase: Insert Post": {
"main": [
[
{
"node": "\ud83d\udd27 Preparar Tags",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd27 Preparar Tags": {
"main": [
[
{
"node": "\ud83d\udcca Supabase: Insert Tags",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcca Supabase: Insert Tags": {
"main": [
[
{
"node": "Create a file",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\ude80 dev.to: Publicar": {
"main": [
[]
]
},
"Hacker News RSS": {
"main": [
[
{
"node": "\ud83d\udd27 Parsear RSS",
"type": "main",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "\ud83d\udd27 Parsear Art\u00edculo",
"type": "main",
"index": 0
}
]
]
},
"OpenRouter 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
}
]
]
},
"OpenRouter Chat Model1": {
"ai_languageModel": [
[
{
"node": "Structured Output Parser",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Create a file": {
"main": [
[
{
"node": "\ud83d\ude80 dev.to: Publicar",
"type": "main",
"index": 0
}
]
]
}
},
"meta": {
"templateCredsSetupCompleted": true
},
"_documentation": {
"name": "AI Blog Creator",
"version": "1.0",
"description": "Reads trending topics from Hacker News RSS, generates bilingual technical articles with AI (OpenRouter), publishes to GitHub (triggers site rebuild), inserts metadata in Supabase, and publishes as draft on dev.to.",
"trigger": "Schedule: Mon/Thu 8am + Manual",
"rss_source": "Hacker News Frontpage (https://hnrss.org/frontpage)",
"ai_model": "OpenRouter (z-ai/glm-4.5-air:free or configured model)",
"placeholders_to_fill": [
"{{ DEVTO_API_KEY }} \u2014 dev.to Settings \u2192 Extensions \u2192 API Keys",
"{{ SUPABASE_SERVICE_ROLE_KEY }} \u2014 Supabase Dashboard \u2192 Settings \u2192 API \u2192 Legacy \u2192 service_role",
"{{ SUPABASE_URL }} \u2014 Supabase Dashboard \u2192 Settings \u2192 API \u2192 Project URL",
"{{ CREDENTIAL_ID }} \u2014 Auto-assigned by n8n when credentials are configured"
],
"credentials_required": [
"OpenRouter account (n8n credential type: OpenRouter API)",
"Mgobeaalcoba Github (n8n credential type: GitHub OAuth2)"
]
}
}
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.
githubOAuth2ApiopenRouterApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
How this works
This workflow automates the creation and publishing of AI-generated blog posts, saving content creators hours of manual research and writing each week. It fetches fresh ideas from RSS feeds, uses an AI agent via lmChatOpenRouter to craft engaging articles, and seamlessly posts them to platforms like dev.to and your Supabase database. Ideal for bloggers, marketers, or solopreneurs who want consistent online presence without the grind, the key step involves the AI intelligently structuring content from parsed sources into polished posts complete with tags.
Use this workflow when you need regular, hands-off content updates from reliable news sources, such as twice-weekly posts on Mondays and Thursdays at 8am via its cron trigger. Avoid it for highly customised or niche topics requiring human oversight, or if your volume exceeds what GitHub integration can handle for version control. Common variations include swapping RSS feeds for Twitter trends or adding email notifications after publishing to dev.to.
About this workflow
01-Ai-Blog-Creator. Uses httpRequest, agent, lmChatOpenRouter, outputParserStructured. Scheduled trigger; 14 nodes.
Source: https://github.com/Mgobeaalcoba/Mgobeaalcoba.github.io/blob/main/docs/automations/01-ai-blog-creator.json — 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.
Complete PostgreSQL-backed system: Keyword scoring → AI research → Multi-part content generation → fal.ai Nano Banana image generation → WordPress publishing
This n8n workflow turns a script and character/setting description from Google Sheets into a complete stitched UGC-style video ad, fully automated from intake to final delivery.
This cutting-edge n8n workflow is a comprehensive automation solution designed to streamline various Instagram operations. It combines an intelligent AI chatbot for direct message management, automate
Viral Video Agent. Uses lmChatOpenRouter, httpRequest, googleSheets, googleDrive. Scheduled trigger; 44 nodes.
Author: Jadai Kongolo