AI News

  • Loading...
Browsing Category " n8n tutorial "

n8n tutorial - Lesson 28: Build a RAG Chatbot with n8n Pinecone and Telegram

n8n tutorial - Lesson 28: Build a RAG Chatbot with n8n Pinecone and Telegram

Hi everyone, in this post I'll show you how to build a RAG chatbot that answers questions over Telegram using n8n, Pinecone, and Claude Haiku — a practical n8n RAG pipeline tutorial from real session notes. This is part of the n8n Workflow Automation Tutorial series, and by the end you'll have a working Telegram bot that queries your Pinecone knowledge base on demand.

How to do:

Step 1 — Prepare the RAG Query Sub-Workflow

Before building the Telegram-facing workflow, you need to make your existing RAG query workflow callable by other workflows in n8n.
  1. Open your existing RAG query workflow — in this series it is named T8-B2-RAG-Query.
  2. Add a new trigger node: search for and select When Executed by Another Workflow.
  3. Inside that trigger node, define one input field:
    • Field name: query
    • Field type: String
  4. Save the workflow and set its status to Active.

Note — The Call n8n Workflow tool node only lists sub-workflows that have a When Executed by Another Workflow trigger. If your workflow uses a Chat Trigger or Manual Trigger only, it will not appear in the tool's dropdown. You must add this trigger and declare the input schema for the workflow to be usable as a tool.

Step 2 — Create the RAG Telegram Workflow

This step builds the main workflow — T8-B3-RAG-Telegram — that receives a Telegram message and routes it through an AI Agent with Pinecone-backed RAG.
  1. Create a new workflow and name it T8-B3-RAG-Telegram.
  2. Add a Telegram Trigger node as the entry point. Configure it with your Telegram bot token so it listens for incoming messages.
  3. Add an AI Agent node and connect it to the Telegram Trigger output.
    • Set the model to Claude Haiku 4.5 (or your preferred model credential).
    • Pass the user's message from the trigger as the agent's input prompt.
  4. Inside the AI Agent's Tools section, add a Call n8n Workflow tool.
  5. In the Call n8n Workflow tool, select T8-B2-RAG-Query from the workflow list and map the query field to the incoming user message.
  6. Add a Telegram node at the end, set the action to Send Message, and map the Chat ID and the AI Agent's output text as the message body.
  7. Save and set the workflow to Active.

Tip — When mapping the Chat ID for the reply, use the value from the Telegram Trigger output — typically $json.message.chat.id. This ensures the bot always replies to the correct conversation thread.

Step 3 — Test the End-to-End RAG Chatbot

With both workflows active, send a real message through Telegram to confirm the full pipeline works.
  1. Open Telegram and find your bot.
  2. Send a question that is covered by your Pinecone knowledge base.
  3. Confirm that:
    • The Telegram Trigger fires and passes the message to the AI Agent.
    • The AI Agent calls T8-B2-RAG-Query with the query field populated.
    • The RAG query retrieves relevant context from Pinecone and returns it to the agent.
    • The agent composes a response and the Telegram send node delivers it back to the chat.
  4. Check the execution logs in n8n for each workflow to verify no errors occurred.

Tip — If the bot replies but the answer is not grounded in your knowledge base, check that the T8-B2-RAG-Query workflow is returning results correctly by running it manually with a test query value first.

Step 4 — Set Up a Cloudflare Named Tunnel (for a Stable Webhook URL)

A named Cloudflare Tunnel gives your local n8n instance a permanent public HTTPS URL, so Telegram webhooks never break on restart — unlike Quick Tunnel which generates a new URL every time.
  1. Log in to your Cloudflare account and add your domain (e.g. dan14.vn) on the free plan.
  2. At your domain registrar, replace the existing nameservers with the two Cloudflare nameservers provided — for example:
    • brenna.ns.cloudflare.com
    • hasslo.ns.cloudflare.com
  3. Wait for DNS propagation — typically 1–2 hours but can take up to 24 hours. Cloudflare will send a confirmation email when the domain is active.
  4. Once you receive the "your domain is now active on Cloudflare" email, open a terminal and run:
    1. cloudflared tunnel login — a browser window opens; select your domain and click Authorize.
    2. cloudflared tunnel create n8n-tunnel — note the Tunnel ID that is returned.
    3. cloudflared tunnel route dns n8n-tunnel n8n.dan14.vn — this creates a CNAME record in your Cloudflare DNS automatically.
  5. Create the tunnel config file at C:\Users\<user>\.cloudflared\config.yml with the following content:
    tunnel: <TUNNEL_ID>
    credentials-file: C:\Users\<user>\.cloudflared\<TUNNEL_ID>.json
    ingress:
      - hostname: n8n.dan14.vn
        service: http://localhost:5678
      - service: http_status:404
  6. Start the tunnel by running: cloudflared tunnel run n8n-tunnel

Note — The last ingress rule — service: http_status:404 — is required as a catch-all. Cloudflare will reject the config if no catch-all rule is present.

Step 5 — Update n8n Webhook URL and Restart

After the tunnel is live, update n8n's environment config so all webhooks use the new permanent domain.
  1. Open your Docker Compose file at D:\n8n\docker-compose.yml.
  2. Find the WEBHOOK_URL environment variable and set it to: https://n8n.dan14.vn
  3. Save the file and restart Docker:
    • Run docker compose down then docker compose up -d in the same directory.
  4. In n8n, deactivate and reactivate any webhook-based workflows — for example T7-B2-Telegram-Chatbot and T8-B3-RAG-Telegram — so they re-register with the new URL.

Production tip — Always deactivate then reactivate webhook workflows after changing WEBHOOK_URL. n8n caches the webhook registration URL at activation time, so existing active workflows will still point to the old address until you cycle them.

Key Lessons from This Session

  1. Sub-workflows must have a "When Executed by Another Workflow" trigger to appear as a callable tool. Chat Trigger and Manual Trigger are not recognized by the Call n8n Workflow tool node — you must add this specific trigger and declare the input schema.
  2. Named Tunnels require your domain to be managed inside Cloudflare DNS. Quick Tunnel works without a domain but the URL changes on every restart, breaking all registered webhooks.
  3. The Cloudflare tunnel setup order matters: add domain → change nameservers → wait for propagation → login with cloudflared → create tunnel → route DNS → write config → run tunnel.
  4. Reactivating workflows after a webhook URL change is mandatory. n8n does not automatically re-register webhooks when the environment variable changes.

Conclusion:

In this n8n RAG pipeline tutorial, you built a fully functional RAG chatbot on Telegram by chaining a Telegram Trigger, an AI Agent, and a callable RAG sub-workflow backed by Pinecone — a key milestone in this n8n workflow automation series. You also learned how to give your local n8n instance a stable public URL using a Cloudflare Named Tunnel, which is essential for reliable webhook-based workflows in production. Next session, the focus shifts to completing the Named Tunnel setup and verifying the permanent webhook URL end-to-end.

If you have any questions, feel free to leave a comment below. Thank you!

Tags: n8n RAG pipeline tutorial, n8n tutorial, n8n workflow automation, Telegram chatbot n8n, Pinecone n8n integration, Cloudflare tunnel n8n, AI agent n8n, n8n sub-workflow

Maybe you are interested!

n8n tutorial - Lesson 27: Connect n8n to Telegram via Cloudflare Tunnel

n8n tutorial - Lesson 27: Connect n8n to Telegram via Cloudflare Tunnel

Hi everyone, in this post we'll walk through how to connect n8n to Telegram using a Cloudflare Tunnel — making your n8n telegram chatbot reachable from the internet without a VPS. This is part of the n8n Workflow Automation Tutorial series and covers the exact setup used to activate the T7-B2-Telegram-Chatbot workflow in a real session.

How to do:

Step 1 — Install Cloudflare Tunnel (cloudflared)

You need cloudflared installed locally to create a public tunnel to your n8n instance running on localhost:5678.
  1. Open a terminal and run: winget install Cloudflare.cloudflared
  2. After installation, start a quick tunnel with: cloudflared tunnel --url http://localhost:5678
  3. Copy the generated public URL — it looks like https://random-name.trycloudflare.com. You will need this in the next step.

Note — This is a "quick tunnel." The URL changes every time you restart cloudflared. That means you must update WEBHOOK_URL and restart n8n each time. A Named Tunnel fixes this — covered at the end of this post.

Step 2 — Set WEBHOOK_URL in Docker Compose

n8n needs to know its own public URL so it can register the correct webhook address with Telegram.
  1. Open your docker-compose.yml file in a text editor.
  2. Under the environment section for your n8n service, add or update:
    • WEBHOOK_URL=https://your-tunnel-url.trycloudflare.com
  3. Save the file, then restart n8n by running: docker compose down && docker compose up -d

Tip — If you skip this step, n8n will still register a localhost address with Telegram, and the webhook will never receive messages. Always set WEBHOOK_URL before activating any Telegram workflow.

Step 3 — Build the Telegram Chatbot Workflow (T7-B2-Telegram-Chatbot)

This workflow receives a user message from Telegram, passes it to an AI Agent, and sends the reply back — the core n8n telegram chatbot pattern.
  1. In n8n, create a new workflow named T7-B2-Telegram-Chatbot.
  2. Add a Telegram Trigger node as the starting node. This registers a webhook with Telegram automatically when the workflow is activated.
  3. Add an AI Agent node (using Claude Haiku 4.5 or your preferred model) connected to the Telegram Trigger.
  4. In the AI Agent node, set Source for Prompt to Define below, then set the prompt expression to: {{ $json.message.text }}
    • This extracts the actual text the user typed in Telegram.
  5. Do not add a Simple Memory node — Telegram Trigger uses a session concept, but Simple Memory caused issues in testing and was removed.
  6. Add a Telegram node at the end to send the AI Agent's reply back to the user.

Tip — The prompt expression {{ $json.message.text }} is the correct path for Telegram Trigger output. If you use a generic {{ $json.text }} it will return empty and the agent will have no input to work with.

Step 4 — Activate the Workflow

Activating the workflow registers the webhook URL with Telegram — this is the step that makes everything live.
  1. Make sure cloudflared is still running and your n8n instance is up with the correct WEBHOOK_URL set.
  2. In the workflow editor, click the Inactive toggle in the top-right corner to set the workflow to Active.
  3. n8n will call the Telegram API to register the webhook at your Cloudflare Tunnel URL automatically.
  4. Open Telegram, find your bot, send a message, and verify the AI Agent replies.

Note — Every time the Cloudflare quick tunnel restarts and gives you a new URL, you must: update WEBHOOK_URL in docker-compose.yml, restart n8n, then toggle the workflow Inactive → Active again. This re-registers the new URL with Telegram.

Step 5 — Understand the Quick Tunnel Limitation and Next Steps

The quick tunnel works perfectly for testing your n8n workflow automation, but it has one real-world drawback you need to plan around.
  1. The problem: The quick tunnel URL (e.g. https://random-name.trycloudflare.com) changes on every restart of cloudflared.
  2. Each URL change requires three actions:
    • Update WEBHOOK_URL in docker-compose.yml
    • Restart n8n with docker compose down && docker compose up -d
    • Toggle the Telegram workflow Inactive then Active to re-register the webhook
  3. Long-term fix — Named Cloudflare Tunnel: A named tunnel gives you a fixed subdomain (e.g. https://n8n.yourdomain.com) that never changes. Setup requires a Cloudflare account with a domain attached.
  4. Alternative — VPS deployment: When you deploy n8n on a VPS, the Webhook node automatically gets a public URL. You no longer need cloudflared at all.
    • Note: Oracle Cloud free tier signup from some regions (e.g., Vietnam) can be difficult — factor this into your planning.

Production tip — For a stable n8n telegram chatbot in production, a Named Cloudflare Tunnel or a VPS is strongly recommended. The quick tunnel is only suitable for development and testing sessions.

Key Lessons from This Session

  1. Always set WEBHOOK_URL before activating a Telegram workflow. Without it, n8n registers a localhost address that Telegram cannot reach.
  2. Quick Cloudflare Tunnel URLs are temporary. The URL changes on every restart — you must update WEBHOOK_URL, restart n8n, and re-activate the workflow each time.
  3. Use {{ $json.message.text }} for the AI Agent prompt in a Telegram Trigger workflow. This is the correct data path from the Telegram Trigger output.
  4. Remove Simple Memory when using a Telegram Trigger. Simple Memory added complexity without benefit in this setup and caused issues during testing.
  5. The Webhook node in n8n does not create a public URL by itself. It only creates an endpoint — you still need cloudflared, ngrok, or a VPS to expose it publicly. On a VPS, the public URL is automatic.
  6. Named Cloudflare Tunnel or VPS is the correct production path. The quick tunnel is a development shortcut, not a stable deployment strategy.

Conclusion:

In this n8n tutorial, we connected a local n8n instance to Telegram using a Cloudflare Tunnel, activated the T7-B2-Telegram-Chatbot workflow, and got a real AI-powered chatbot responding to Telegram messages. The key takeaway for this n8n workflow automation pattern is that WEBHOOK_URL and tunnel stability are the two variables that determine whether your bot stays online. In the next post in this series, we'll merge the Telegram Chatbot with a RAG pipeline so the bot can answer questions from a knowledge base.

If you have any questions, feel free to leave a comment below. Thank you!

Tags: n8n telegram chatbot, n8n tutorial, n8n workflow automation, cloudflare tunnel n8n, n8n webhook setup, n8n docker webhook, n8n AI agent, n8n beginner tutorial

Maybe you are interested!

n8n tutorial - Lesson 26: n8n Multi-Tool AI Chatbot: Internal Knowledge Assistant

n8n tutorial - Lesson 26: n8n Multi-Tool AI Chatbot: Internal Knowledge Assistant

Hi everyone, in this session we're building a multi-tool internal knowledge assistant using an n8n chatbot workflow — an AI agent that pulls data from multiple Google Sheets sources and synthesizes it into a single natural-language report. This is part of our ongoing n8n workflow automation tutorial series, and it's where the real power of agents over fixed workflows becomes clear.

How to do:

Step 1 — Upgrade Your Existing Agent with Three New Tools

Open your base agent workflow (here: T7-B1-First-Agent) and add three new Google Sheets tool nodes — each connecting to a different data source.
  1. Open the workflow and click the + button inside the AI Agent node's Tools section to add a new tool.
  2. Add the first tool: set Name to get_rejected_content, connect it to the Google Sheets node pointing to spreadsheet T6-Rejected, tab Sheet1.
  3. Add the second tool: set Name to get_youtube_performance, connect it to spreadsheet T5-Performance-Snapshots, tab Snapshots.
  4. Add the third tool: set Name to get_comments_queue, connect it to spreadsheet T5-Comments-Queue, tab Queue.
  5. For each tool, fill in the Description field immediately — describe what data it returns so the agent can decide when to call it.

Note — Always configure all four fields — Name, Description, Operation, and Document/Sheet — in one go. Skipping Description means the agent won't know when to invoke the tool, and you'll have to go back and re-edit each node.

Step 2 — Add a System Prompt to Define the Agent's Role

The System Message field in the AI Agent node is where you define the agent's persona, default language, and response behavior — without it, the agent gives raw data dumps instead of useful summaries.
  1. Click the AI Agent node to open its settings, then locate the System Message field.
  2. Write a prompt that:
    • Declares the agent's role (e.g., "You are an internal assistant for a content team.")
    • Specifies the response language appropriate for your team.
    • Instructs the agent to summarize insights rather than list raw data.
  3. Save the node after entering the system prompt.

Tip — The system prompt is the single biggest lever for output quality. Telling the agent to "summarize insights, not raw rows" transforms the response from a data table into an actionable briefing.

Step 3 — Test the Daily Briefing (Multi-Tool Parallel Call)

Send one natural-language question to verify the agent calls all three tools and synthesizes a combined report.
  1. Open the workflow's test chat or trigger a manual execution.
  2. Send the message: Give me a full system overview report for today.
  3. Watch the execution log — the agent should call all three tools in parallel: get_rejected_content, get_youtube_performance, and get_comments_queue.
  4. Verify the output contains all four sections:
    • YouTube performance summary
    • Blog/rejected content status
    • Comment queue overview
    • A "needs action" section highlighting items requiring follow-up

Note — This is the core advantage of an agent over a fixed n8n workflow automation: one free-form question triggers dynamic multi-tool reasoning. A scheduled workflow would require you to hardcode exactly which nodes run and in what order.

Step 4 — Build the Telegram Chatbot Workflow

Create a new workflow named T7-B2-Telegram-Chatbot that chains a Telegram trigger, the AI agent, and a Telegram reply node.
  1. Create a new workflow and add a Telegram Trigger node as the entry point — this listens for incoming messages via webhook.
  2. Add an AI Agent node and configure it with the same four tools and system prompt from Steps 1–2.
  3. Add a Simple Memory node and connect it to the AI Agent's memory input so conversation context persists across messages.
  4. Add a Telegram node (Send Message operation) and wire it to the agent's output to return the response to the user.
  5. Set the model to Claude Haiku 4.5 (or your preferred model) in the AI Agent node.

Step 5 — Handle the Localhost / HTTPS Limitation for Telegram

Telegram Trigger uses a webhook, which requires a publicly accessible HTTPS URL — this is the wall you'll hit when running n8n locally.
  1. Understand the constraint:
    • Telegram webhooks only POST to HTTPS public URLs.
    • A local localhost n8n instance has no public URL, so Telegram can't reach it.
  2. Attempt with ngrok (common workaround):
    • Install ngrok and get an HTTPS tunnel URL.
    • Open docker-compose.yml and add the environment variable N8N_WEBHOOK_URL=https://your-ngrok-url.
    • Restart the container: run docker-compose down && docker-compose up -d.
    • Verify the variable was applied: run docker exec <container_name> env | findstr N8N_WEBHOOK_URL.
  3. Accept the result: ngrok free tier is too unstable for Telegram webhooks in practice — tunnels disconnect, Telegram stops receiving events.
  4. Leave T7-B2-Telegram-Chatbot as Inactive until you deploy to a VPS with a real domain and HTTPS certificate.

Production tip — The real fix is deploying n8n on a VPS with a proper domain and HTTPS — the same requirement as Human-in-the-Loop workflows. ngrok is fine for quick testing but not reliable for any trigger that depends on an external service pushing data to you.

Note — When working with Docker, remember that the container name shown by docker ps is not always the same as the service name in docker-compose.yml. Always use docker ps to find the real container name before running docker exec commands.

Step 6 — Audit API Costs and Decide Which Workflows to Keep Active

Some scheduled workflows call an AI model on every item every run — these accumulate cost fast and should be deactivated when not needed.
  1. Identify high-cost workflows to deactivate:
    • T5-B2-Comment-Pipeline — runs every 30 minutes, calls Claude for every comment. Primary cost driver.
    • T2-B4-Email-Classifier — runs every 15 minutes, calls Claude for every email.
  2. Identify safe workflows to keep active (no AI calls or infrequent calls):
    • T6-B1-Error-Handler — no AI node.
    • T5-B2b-Reply-Sender — no AI node.
    • T5-B3b-Title-Updater — no AI node.
    • T5-B4-Performance-Insight — weekly schedule, one AI call per run.
    • T5-B7-Weekly-Digest — weekly schedule, one AI call per run.
  3. Toggle the high-cost workflows to Inactive in the n8n dashboard until you're ready to run them intentionally.

Tip — A per-item AI call inside a frequent schedule is the fastest way to drain API credits. The rule: if a workflow calls an AI model in a loop and runs more than once per hour, treat it as high-cost by default and run it manually or reduce its frequency.

Key Lessons from This Session

  1. Always fill in the tool Description field immediately. The agent uses this text to decide which tool to call — a blank description makes the tool invisible to the agent's reasoning.
  2. One question → multi-tool parallel execution is the agent's core value. A fixed n8n tutorial workflow can automate a known sequence; an agent handles unknown, flexible queries by choosing tools dynamically.
  3. Telegram Trigger requires a real HTTPS public URL. ngrok free tier is not reliable enough for production webhooks — deploy to a VPS for any external trigger.
  4. Set environment variables in docker-compose.yml, not in the running container. Use docker-compose down && docker-compose up -d to apply changes, and verify with docker exec.
  5. Frequent scheduled workflows with per-item AI calls are the main API cost risk. Audit and deactivate them when not in active use.
  6. Agent vs. fixed workflow is a design choice, not a default. Use agents when the query is unpredictable or requires reasoning; use fixed workflows for known, repeatable automation sequences.

Conclusion:

In this n8n tutorial session, we turned a single-tool agent into a full internal knowledge assistant by connecting it to three Google Sheets data sources, adding a system prompt, and validating multi-tool parallel execution with one natural-language question. We also built a Telegram-connected n8n chatbot workflow, hit the real-world HTTPS limitation of local deployments, and learned how to audit API costs across the entire automation stack. The next session will explore either scheduled agent automation, custom code tools, or a VPS deployment to unlock Telegram and Human-in-the-Loop workflows — all key milestones in n8n workflow automation.

If you have any questions, feel free to leave a comment below. Thank you!

Tags: n8n chatbot workflow, n8n tutorial, n8n workflow automation, AI agent n8n, n8n Google Sheets, Telegram bot n8n, n8n multi-tool agent, n8n localhost webhook

Maybe you are interested!

n8n tutorial - Lesson 25: Build Your First AI Agent in n8n: ReAct Pattern Explained

n8n tutorial - Lesson 25: Build Your First AI Agent in n8n: ReAct Pattern Explained

Hi everyone, in this n8n AI agent tutorial, you'll build your first working AI agent using the ReAct pattern — complete with multiple tools, memory, and an orchestrator that triggers sub-workflows. This is part of the n8n Workflow Automation Tutorial series, and it's the session where things shift from simple automation to agents that can reason and act on their own.

How to do:

Step 1 — Understand the ReAct Pattern and Agent Architecture

Before building anything, you need to know what makes an agent different from a regular n8n workflow — the ReAct loop is the core of it.
  1. The ReAct pattern stands for Thought → Action → Observation, repeated in a loop until the agent reaches a final answer.
  2. An n8n AI agent has exactly three components:
    • Agent node — the brain that decides what to do
    • Tools — actions the agent can call (HTTP requests, spreadsheets, Telegram, etc.)
    • Memory — stores conversation context across turns
  3. Know the difference between the three execution models:
    • Regular workflow — fixed, hard-coded flow; great for simple, predictable tasks
    • Chain — LLM calls in sequence, but no tool-calling or dynamic decisions
    • Agent — decides which tool to call, in what order, and handles unexpected situations using natural language

Note — A regular Schedule + Google Sheets + Telegram workflow can send automated reports — but it only follows the exact path you hard-coded. An agent handles variable flows: if a step fails or a new condition appears, it reasons through it instead of breaking.

Step 2 — Create the Workflow and Add the Chat Trigger

Create a new workflow named T7-B1-First-Agent and set up the entry point.
  1. In n8n, click New Workflow and name it T7-B1-First-Agent.
  2. Add a Chat Trigger node as the starting node — this lets you send messages to the agent directly from the n8n chat interface during testing.
  3. This workflow runs in Manual/Chat mode, so you do not need to set it to Active.

Step 3 — Add the AI Agent Node and Configure the Model

The AI Agent node is the core of this n8n AI agent tutorial — wire it to the Chat Trigger and set the language model.
  1. Add an AI Agent node and connect it to the Chat Trigger output.
  2. Inside the Agent node, set the model to Claude Haiku 4.5 as the default — it's fast and cost-efficient for most tool-calling tasks.
  3. Upgrade to Claude Sonnet 4.6 on demand when a task requires stronger reasoning (complex multi-step orchestration, ambiguous instructions).

Tip — Starting with Haiku keeps costs low during development and testing. Only swap to Sonnet when you notice the agent making wrong tool choices or failing to chain steps correctly.

Step 4 — Attach Simple Memory

Memory lets the agent remember context across messages in the same conversation — without it, every message is treated as a fresh start.
  1. Inside the AI Agent node, find the Memory sub-section and add a memory module.
  2. Select Simple Memory (this is the current name in newer n8n versions — older guides may call it "Window Buffer Memory", but the functionality is identical).
  3. To verify memory works, test this sequence:
    • Send: "What is 500 USD in VND?"
    • Then send: "Double that amount."
    • The agent should resolve "that amount" as 500 USD from context and return the doubled result.

Note — The name change from "Window Buffer Memory" to "Simple Memory" is a known n8n UI update. If you follow an older n8n tutorial and can't find the node, look for Simple Memory in the memory selector instead.

Step 5 — Add Tool 1: Calculator

The Calculator tool is built into n8n and requires zero configuration — it's the easiest way to confirm your agent can call tools.
  1. In the AI Agent node, go to the Tools section and click Add Tool.
  2. Select Calculator from the list — no credentials or additional settings needed.
  3. Test it by asking: "What is 1234 multiplied by 56?" — the agent should invoke the Calculator tool and return the correct answer.

Step 6 — Add Tool 2: HTTP Request for Exchange Rates

This tool gives the agent the ability to fetch live exchange rate data from a free public API — no authentication required.
  1. Add a new tool and select HTTP Request.
  2. Set the tool name to get_exchange_rate.
  3. Configure the request:
    • Method: GET
    • URL: https://api.exchangerate-api.com/v4/latest/USD
    • Authentication: None (this is a public, free endpoint)
  4. Write a clear tool description so the agent knows when to use it — for example: "Get the latest USD exchange rates against all major currencies."

Tip — The tool description is not cosmetic — the agent reads it to decide whether to call this tool for a given user request. Write it as a one-sentence summary of what the tool returns and when it's relevant.

Step 7 — Add Tool 3: Google Sheets for Pending Blog Topics

This tool connects the agent to a real Google Sheet to retrieve blog topics that are queued for writing.
  1. Add a new tool and select Google Sheets.
  2. Set the tool name to get_pending_topics.
  3. Configure it to read from the sheet named T4-B5-Blog-Topics.
  4. Set a filter so it only returns rows where the status column equals pending.
  5. Connect your existing Google Sheets credential — reuse whatever credential you set up in earlier sessions of this n8n workflow automation series.

Step 8 — Add Tool 4: Telegram for Sending Reports

This tool lets the agent send a summary message to a Telegram chat after completing a task — the key here is using $fromAI() correctly.
  1. Add a new tool and select Telegram, action: Send Message.
  2. Set the tool name to send_telegram_report.
  3. Configure the Chat ID field with your Telegram chat ID (static value — does not change).
  4. For the Text field, do NOT leave it blank — n8n marks this field as required and will throw a validation error. Set it to:
    $fromAI('message', 'The message to send')

Note — The $fromAI('param', 'description') syntax tells n8n to let the agent decide what value to fill in at runtime. Use it for any field whose value depends on what the agent is doing — message text, topic names, row numbers, etc. Without it, n8n either errors out (required fields) or always sends the same static text.

Step 9 — Test Multi-Tool Calling and Memory Together

Run a real test that forces the agent to call more than one tool in a single response — this confirms the ReAct loop is working end-to-end.
  1. Open the Chat panel and send: "What are the pending blog topics, and what is 500 USD in VND?"
  2. Watch the execution — the agent should call both get_pending_topics and get_exchange_rate in the same run, either in parallel or sequentially.
  3. Follow up with: "Double the USD amount." — the agent should remember 500 USD from the previous message and return 1000 USD without you repeating it.

Tip — If the agent only calls one tool when you expected two, rephrase the request to make both needs explicit. The agent uses your prompt and the tool descriptions together to decide what to call — vague prompts produce vague tool choices.

Step 10 — Add the Orchestrator Tool: Call n8n Workflow Tool

This is where the agent becomes an orchestrator — it can trigger an entire sub-workflow as if it were just another tool.
  1. Add a new tool and select Call n8n Workflow Tool (older guides may call this "Execute Sub-workflow" — the correct name in the Agent node's tool menu is Call n8n Workflow Tool).
  2. Set the tool name to generate_blog_content.
  3. Point it at the existing sub-workflow T6-Content-Child-Blog (built in a previous session of this n8n tutorial series).
  4. For any input fields the sub-workflow expects (topic, row number, etc.), set the values using $fromAI('field_name', 'description') so the agent decides what to pass at runtime.
  5. Write a tool description like: "Generate and publish a blog post for a given pending topic. Pass the topic name and row number from the Google Sheet."

Step 11 — Test the Full Orchestration Flow

Send a single natural language command and watch the agent read the sheet, trigger the content factory, publish to Blogger, and report back.
  1. In the Chat panel, send something like: "Pick the first pending topic from the sheet and publish a blog post for it."
  2. The agent should execute this sequence automatically:
    • Call get_pending_topics → read the Google Sheet
    • Call generate_blog_content → trigger T6-Content-Child-Blog
    • Sub-workflow generates content and posts to Blogger
    • Call send_telegram_report → send you the Post ID and published link
  3. Check the final Telegram message — it should contain the full result: Post ID and a live link to the published post.

Production tip — The agent decides when to call the orchestrator tool and what data to pass — you don't hard-code that logic anywhere. This is the fundamental difference between agent-based automation and a regular n8n workflow: the flow isn't predetermined, it's reasoned at runtime.

Step 12 — Understand the Human-in-the-Loop Limitation

During this session, Telegram Send and Wait was tested as a way to pause the agent and wait for human approval before continuing.
  1. Add a Telegram: Send and Wait node to your workflow and attempt to use it as an approval gate.
  2. You will find it does not work on localhost:5678 — Telegram's callback cannot reach a local n8n instance, so the workflow hangs indefinitely waiting for a response that never arrives.
  3. This is a known localhost limitation:
    • Human-in-the-Loop via Telegram requires n8n to have a public URL
    • Solutions: use ngrok to expose localhost, or deploy n8n to a VPS
  4. For now, remove the Send and Wait node and note it for a future session when n8n is deployed with a public URL.

Note — This is a real production consideration, not just a tutorial limitation. Any n8n workflow that needs webhook callbacks — including Human-in-the-Loop approvals — must run on a publicly accessible URL. Local development environments will always block these flows.

Key Lessons from This Session

  1. ReAct = Thought → Action → Observation loop. The agent repeats this cycle until it has a final answer — it doesn't execute a fixed path like a regular workflow.
  2. An n8n AI agent has three components: Agent node, Tools, and Memory. Remove any one of these and you have a chain or a regular workflow, not an agent.
  3. Use $fromAI('param', 'description') for any field the agent must decide at runtime. Required fields left blank will cause a validation error; static values defeat the purpose of using an agent.
  4. Tool descriptions are instructions, not labels. The agent reads them to decide which tool to invoke — write them as clear, one-sentence functional summaries.
  5. "Call n8n Workflow Tool" is the correct node name for triggering sub-workflows from an agent. The older name "Execute Sub-workflow" no longer appears in the Agent tool menu.
  6. "Simple Memory" is the current name for what older guides call "Window Buffer Memory." The functionality is identical — only the UI label changed.
  7. Human-in-the-Loop via Telegram requires a public URL. Localhost cannot receive Telegram callbacks; use ngrok or a VPS deployment for this feature.
  8. An agent beats a regular workflow when the flow cannot be predetermined. For simple, fixed-path tasks, a regular n8n workflow automation is still the better choice.

Conclusion:

In this n8n AI agent tutorial, you built a full ReAct-pattern agent with four tools, working memory, and an orchestrator that triggers a sub-workflow from a single natural language command — going from understanding the theory to watching it publish a real blog post and report back on Telegram. The key shift is moving from hard-coded workflow logic to an agent that reasons, decides, and acts dynamically. In the next session of this n8n workflow automation tutorial series, you'll go further with advanced agent patterns: an internal chatbot that queries live data, a scheduled autonomous agent, or custom tools built with code.

If you have any questions, feel free to leave a comment below. Thank you!

Tags: n8n AI agent tutorial, n8n tutorial, n8n workflow automation, ReAct pattern n8n, n8n tools setup, AI agent orchestrator, n8n beginner guide, n8n automation tips

Maybe you are interested!

n8n tutorial - Lesson 24: Quality Gate Pattern in n8n: AI Review Before Publishing

n8n tutorial - Lesson 24: Quality Gate Pattern in n8n: AI Review Before Publishing

Hi everyone, in this post we're building a Quality Gate inside an n8n Content Factory workflow — an AI-powered review layer that blocks low-quality content before it ever gets published. This is a core pattern in n8n quality control automation and one of the most practical additions you can make to any content pipeline.

How to do:

Step 1 — Design the AI Review Checklist (Blog + YouTube)

Before adding any nodes, define exactly what the AI will check and what counts as a passing score.
  1. For the Blog review, define 5 criteria: word_count, has_headings, has_keyword, no_placeholder, title_length.
  2. For the YouTube review, define 5 criteria: title_length, description_length, has_tags, has_timestamps, no_empty_field.
  3. Set the pass threshold at ≥ 4 out of 5 criteria met.
  4. Require the AI to return a structured JSON object with three fields: pass, score, and notes.

Tip — Locking down the output schema before building the nodes saves debugging time later. The pass field will drive your IF node, score goes to the rejected log, and notes tells you exactly why a piece failed.

Step 2 — Insert AI Review Nodes into Each Child Workflow

Add an AI review node to both the Blog and YouTube child workflows, placing each one at the right point in the chain.
  1. In T6-Content-Child-Blog, insert an AI Review Blog node between the Format HTML node and the POST Draft Blogger node.
  2. In T6-Content-Child-YouTube, insert an AI Review YouTube node at the equivalent position — after content is fully formatted.
  3. In the User message of AI Review YouTube, wrap array fields with JSON.stringify():
    • Tags: JSON.stringify($('YouTube SEO').item.json.output.tags)
    • Timestamps: JSON.stringify($('YouTube SEO').item.json.output.timestamps)

Note — If you pass an array directly into a User message expression without JSON.stringify(), n8n renders it as [object Object] and the AI cannot read the data. Always stringify arrays before injecting them into prompt strings.

Step 3 — Add Structured Output Parsers

Attach a Structured Output Parser to each AI review node so the response always comes back as clean, typed JSON.
  1. For AI Review Blog: use the Schema (JSON string) method in the parser.
  2. For AI Review YouTube: use the Generate From JSON Example method in the parser.
  3. Provide an example JSON like {"pass": true, "score": 4, "notes": "missing keyword"} so n8n infers the correct types.

Note — These two parser methods produce different output types for the pass field. The Schema method returns "true" as a string; the Generate From JSON Example method returns true as a boolean. You must configure the IF node to match the correct type for each workflow.

Step 4 — Add IF Nodes to Route Pass vs. Fail

Insert an IF node after each review node to split the workflow into a passing branch and a failing branch.
  1. In T6-Content-Child-Blog, add an IF node named Check Pass.
  2. Set its condition on $json.pass:
    • Because the Blog parser returns a string, set the condition to String → equals → "true" — not Boolean.
  3. In T6-Content-Child-YouTube, add an IF node named Check Pass YT.
  4. Set its condition to Boolean → is true because the YouTube parser returns a real boolean.
  5. Connect the True branch of each IF node to the existing publish nodes (POST Draft Blogger, Create Google Doc).
  6. Connect the False branch of each IF node to a Google Sheets Append node targeting the Sheet T6-Rejected.

Tip — Mixing up string "true" and boolean true in IF conditions is one of the most common silent bugs in n8n. If your IF node always routes to the False branch despite the AI passing content, this type mismatch is the first thing to check.

Step 5 — Fix Cross-Node References After IF Node Insertion

After inserting the IF node, all downstream nodes lose their direct $json context — this is a critical gotcha in this n8n tutorial.
  1. Understand what changed: after the IF node, $json inside downstream nodes refers to the IF node's output (only the review result), not the original formatted content.
  2. In POST Draft Blogger, replace any $json.xxx references with explicit cross-node refs, for example:
    • $('Format HTML').item.json.title
    • $('Format HTML').item.json.html_content
  3. Apply the same fix to Create Google Doc — reference the correct upstream node by name for every field it needs.
  4. For the YouTube child workflow, confirm that Create Google Doc is placed after the IF node, not before it. An earlier misplacement caused it to run regardless of review outcome.

Note — Cross-node references like $('NodeName').item.json.field are the reliable way to reach data from any earlier node in the chain. Make this your default approach whenever the data path passes through a branching node like IF, Switch, or Merge.

Step 6 — Fix HTML Content in POST Draft Blogger Body

Passing HTML as a raw JSON string in the request body breaks when the content contains special characters.
  1. Identify the problem: html_content contains double quotes and newlines, which corrupt the raw JSON string body.
  2. Switch the POST Draft Blogger node's body mode from Raw JSON string to Body Parameters (key-value pairs).
  3. Map each field (title, content, labels, etc.) as a separate key-value entry — n8n handles escaping automatically in this mode.

Tip — Whenever you're sending HTML or any user-generated text in an HTTP request body, key-value / Body Parameters mode is safer than raw JSON strings. n8n escapes the values for you, eliminating an entire class of encoding bugs.

Step 7 — Set Up the T6-Rejected Sheet Log

Route failed content to a dedicated Google Sheet so you can review and fix it later.
  1. Create a new Google Sheet named T6-Rejected.
  2. Define these columns: timestamp, topic, score, notes.
  3. In the False branch of each IF node, connect a Google Sheets → Append Row node targeting this sheet.
  4. Map the fields:
    • timestamp: {{ $now }}
    • topic: cross-node ref to the topic field from the trigger
    • score: $json.score
    • notes: $json.notes

Step 8 — Pass row_number into Child Workflows and Fix Mark Done

Matching sheet rows by topic string is fragile; switching to row_number makes the Mark Done update reliable.
  1. Open the child workflow's trigger node (When Executed by Another Workflow) and add a new input field: row_number (type: Number).
  2. In the parent workflow (T6-Content-Factory-Dispatch), open the Call Child Blog Workflow node and click Refresh Input List to see the new field.
  3. Map the parent's row_number value into the new field.
  4. In the Mark Done node, change the row-matching logic from topic string to row_number (number match).

Tip — String-based row matching fails silently when there's a whitespace difference or encoding mismatch between the sheet and the workflow variable — you get "No output data returned" with no clear error. Number-based matching with row_number is deterministic and always safe.

Step 9 — Test End-to-End: Pass and Fail Paths

Run a full test covering both branches to confirm the quality gate works correctly.
  1. Trigger the dispatch workflow with a topic that will produce high-quality content (score ≥ 4).
  2. Verify the True branch executes: Blogger draft is posted, Google Doc is created, Sheet status is updated to done.
  3. Temporarily lower the pass threshold or submit a topic with missing fields to force a fail.
  4. Verify the False branch executes: a new row appears in T6-Rejected with correct timestamp, topic, score, and notes.
  5. Confirm Mark Done updates the correct row (matched by row_number, not topic string).

Key Lessons from This Session

  1. Cross-node refs break after IF nodes. Once your data path passes through a branching node, $json no longer points to earlier content — always use $('NodeName').item.json.field explicitly.
  2. HTML in raw JSON bodies causes silent corruption. Switch to Body Parameters (key-value) mode and let n8n handle escaping automatically.
  3. Structured Output Parser type depends on the method used. Schema JSON string → returns pass as a string; Generate From JSON Example → returns pass as a boolean. Your IF condition must match the actual type.
  4. Child workflow inputs require declaration in the trigger first. The parent cannot pass a new field like row_number until the child's trigger node declares it, followed by a Refresh Input List in the parent.
  5. Use row_number, not topic strings, for sheet row matching. String matching fails silently on whitespace or encoding differences; number matching is deterministic.
  6. Always JSON.stringify arrays in prompt expressions. Arrays passed raw into User message expressions render as [object Object], making them unreadable to the AI model.

Conclusion:

In this n8n workflow automation tutorial, we added a full AI-powered quality gate to a Content Factory — covering checklist design, structured output parsing, IF-based routing, rejected content logging, and reliable row matching. These patterns make your automation genuinely production-ready by catching bad content before it ever reaches a live channel. Next session we move into Week 7 and start building our first AI Agent in n8n.

If you have any questions, feel free to leave a comment below. Thank you!

Tags: n8n quality control automation, n8n tutorial, n8n workflow automation, AI review workflow, content factory n8n, structured output parser n8n, quality gate pattern, n8n IF node

Maybe you are interested!

n8n tutorial - Lesson 23: Production-Ready n8n Workflows: Hardening and Scheduling

n8n tutorial - Lesson 23: Production-Ready n8n Workflows: Hardening and Scheduling

Hi everyone, in this post we're covering how to harden a multi-workflow n8n automation into a true n8n production workflow — adding error handling, scheduling, and live alerting. This is Session 23 of the n8n Workflow Automation Tutorial series, and it's where your Content Factory stops being a manual prototype and starts running reliably on its own.

How to do:

Step 1 — Verify Initial Workflow States

Before making any changes, confirm the starting state of all four workflows to avoid activating them in the wrong order.
  1. Open your n8n dashboard and check that T6-Content-Factory-Dispatch, T6-Content-Child-Blog, and T6-Content-Child-YouTube are all Inactive.
  2. Confirm that T6-B1-Error-Handler is already Active. This is the baseline — the error handler must be live before you test anything.

Note — If any child workflow is Active before you configure its error settings, deactivate it now. You want a clean slate before touching the node settings.

Step 2 — Set "On Error = Continue (using error output)" on Execute Sub-workflow Nodes

This setting prevents a child workflow failure from silently killing the entire parent run — it routes the error into a visible output instead.
  1. Open T6-Content-Factory-Dispatch (the parent workflow) in the editor.
  2. Click the Execute Sub-workflow node that calls T6-Content-Child-Blog.
  3. Go to Settings inside that node and find the On Error dropdown. Set it to Continue (using error output).
  4. Repeat the same three actions for the Execute Sub-workflow node that calls T6-Content-Child-YouTube.
  5. Save the parent workflow after both nodes are configured.

Tip — After selecting Continue (using error output), you will notice a new red output pin appears on the Execute Sub-workflow node. This is expected behavior — it is the error branch. You do not need to connect it to anything right now; it simply means errors will no longer crash the execution silently.

Step 3 — Replace Manual Trigger with a Schedule Trigger

Swapping out the Manual Trigger for a Schedule Trigger is what makes this an automated n8n production workflow instead of a tool you run by hand.
  1. Inside T6-Content-Factory-Dispatch, click the existing Manual Trigger node and delete it.
  2. Add a new Schedule Trigger node from the node panel.
  3. Configure it with the following values:
    • Trigger Interval: Days
    • Hour: 8
    • Minute: 0
  4. Connect the output of Schedule Trigger to the first node in your existing flow (the same connection the Manual Trigger had).
  5. Save the workflow.

Step 4 — Attach the Error Workflow to All Three Workflows

Assigning T6-B1-Error-Handler as the error workflow for each of the three workflows ensures that any unhandled failure fires a Telegram alert automatically.
  1. Open T6-Content-Factory-Dispatch and go to Workflow Settings (the gear icon or top menu).
  2. Find the Error Workflow field and select T6-B1-Error-Handler from the dropdown. Save.
  3. Open T6-Content-Child-Blog, go to Workflow Settings, set Error Workflow to T6-B1-Error-Handler. Save.
  4. Open T6-Content-Child-YouTube, go to Workflow Settings, set Error Workflow to T6-B1-Error-Handler. Save.

Note — The Error Workflow field only accepts workflows that are already Active. Since you verified T6-B1-Error-Handler is Active in Step 1, it will appear in the dropdown without issues.

Step 5 — Activate All Three Workflows in the Correct Order

n8n enforces a strict activation order: child workflows must be Active before the parent, or you will get a not published error when activating the parent.
  1. Activate T6-Content-Child-Blog first — toggle it to Active and confirm the status turns green.
  2. Activate T6-Content-Child-YouTube second — same process.
  3. Activate T6-Content-Factory-Dispatch last — only after both children are confirmed Active.

Production tip — Always follow the child-before-parent activation order whenever you deactivate and reactivate these workflows later. Forgetting this is the most common reason for the not published error in multi-workflow n8n setups.

Step 6 — Run a Clean Production Test

Test the live production execution path (not the UI test button) to confirm everything runs without errors.
  1. Inside T6-Content-Factory-Dispatch (which is now Active), click Execute once to trigger a production-mode run.
  2. Go to the Executions tab and confirm all runs show a Success status.
  3. Check your Telegram channel — if there are no workflow errors, no alert message should arrive. Silence here is correct behavior.

Tip — The UI test button (the flask/beaker icon) does not trigger the Error Workflow even if an error occurs. Only executions running from an Active workflow trigger the Error Handler. Always use Execute once from an Active workflow when you want to test production error alerting.

Step 7 — Inject a Fake Error to Verify Telegram Alerting

Deliberately breaking one child workflow confirms that your error alerting pipeline actually works end-to-end.
  1. Open T6-Content-Child-Blog and locate the HTTP Request node that calls your content endpoint.
  2. Change its URL to an intentionally wrong value (for example, add _broken to the end of the URL).
  3. Save T6-Content-Child-Blog — it remains Active with the bad URL.
  4. Go back to T6-Content-Factory-Dispatch and click Execute once again.
  5. Check Telegram — you should receive an error alert from T6-B1-Error-Handler within seconds. ✅
  6. Go back to T6-Content-Child-Blog, restore the correct URL, and save.
  7. Run Execute once one more time — confirm executions are clean and no Telegram alert fires.

Step 8 — Deactivate the Parent Workflow Until Needed

After the session, deactivate T6-Content-Factory-Dispatch to prevent it from running automatically at 8:00 AM until you are ready.
  1. Toggle T6-Content-Factory-Dispatch to Inactive on the dashboard.
  2. Leave T6-Content-Child-Blog, T6-Content-Child-YouTube, and T6-B1-Error-Handler as Active.
  3. When you want the schedule to run again, toggle the parent back to Active with one click — no other changes needed.

Note — Keeping the children Active while the parent is Inactive costs nothing and means reactivation later is instant. This is a clean pattern for managing n8n workflow automation schedules you want to pause temporarily.

Key Lessons from This Session

  1. Child workflows must be activated before the parent. n8n throws a not published error if the parent is activated while any child is still Inactive. Order: child Blog → child YouTube → parent Dispatch.
  2. The UI test button does not trigger the Error Workflow. You must run from an Active workflow using Execute once to test real error alerting behavior.
  3. "Continue (using error output)" adds a red output pin — this is normal. The pin is the error branch; you do not need to wire it up immediately, but it makes errors visible instead of swallowing them.
  4. Silence on Telegram after a clean run is the correct behavior. If no errors occur, no alert should fire — confirm this during your clean test run.
  5. Deactivating only the parent is enough to pause the schedule. Children and the error handler can stay Active, making re-enabling the whole system a one-click action.

Conclusion:

In this session of the n8n tutorial series, we transformed a manually-run Content Factory into a hardened n8n production workflow with scheduled execution, multi-level error handling, and live Telegram alerting. The end result is a system where failures surface immediately, the schedule is easy to pause and resume, and every layer — parent and children — is protected by the same error handler. Next up, we'll look at adding a quality gate using an AI review checklist before content gets posted, or extending the pattern with a new Email child workflow.

If you have any questions, feel free to leave a comment below. Thank you!

Tags: n8n production workflow, n8n tutorial, n8n workflow automation, n8n schedule trigger, n8n error handling, n8n execute sub-workflow, n8n content factory, workflow automation tutorial

Maybe you are interested!

n8n tutorial - Lesson 22: AI Image Generation in n8n with gpt-image-1

n8n tutorial - Lesson 22: AI Image Generation in n8n with gpt-image-1

Hi everyone, in this session of our n8n workflow automation tutorial series, we cover how to fix broken image generation after OpenAI killed DALL-E, migrate to gpt-image-1, and extend the Content Factory with a YouTube metadata child workflow — all real steps from an actual n8n image generation AI build.

How to do:

Step 1 — Understand Why DALL-E Stopped Working

Before touching any node, confirm the root cause so you fix the right thing.
  1. OpenAI permanently shut down DALL-E 2 and DALL-E 3 on 12 May 2026 (announced 14 November 2025). Any call to model: "dall-e-3" returns error code model_not_found with the message "The model 'dall-e-3' does not exist" — there is no grace period.
  2. The original plan from the previous session was to swap the OpenAI node for an HTTP Request node and simply remove response_format. That partial fix is not enough because the model itself no longer exists.
  3. Web-search to confirm the shutdown date before making any code change — never rely solely on AI knowledge when the situation is time-sensitive.

Note — This is a recurring lesson in production automation: when an AI assistant's training data is older than the current date, always run a quick search to verify before acting on its suggestion.

Step 2 — Migrate the Image Generation Node to gpt-image-1

Replace the dead DALL-E call with gpt-image-1 and update the request body to match the new API contract.
  1. Open your T6-Content-Child-Blog workflow and locate the node that previously called DALL-E (either an OpenAI node or an HTTP Request node).
  2. In the HTTP Request node, set the Body to:
    • model: gpt-image-1
    • prompt: your prompt expression
    • size: 1024x1024
    • n: 1
  3. Remove any response_format field from the body — gpt-image-1 does not accept it and will error if it is present.

Tip — The endpoint URL stays the same (https://api.openai.com/v1/images/generations). Only the model name and body shape change, so keep your existing Authorization header and credentials untouched.

Step 3 — Handle the Base64 Response (Replace the Download Image Node)

gpt-image-1 returns a Base64 string, not a URL — the old Download Image node is now useless and must be replaced.
  1. Understand the critical difference:
    • DALL-E 3 returned: data[0].url — a public URL you could GET directly.
    • gpt-image-1 returns: data[0].b64_json — a raw Base64 string.
  2. Delete (or disable) the Download Image node that previously performed a GET request on the image URL.
  3. Add a Code node named Base64 to Binary immediately after the HTTP Request node.
  4. Inside the Code node, write logic to:
    • Read $json.data[0].b64_json from the previous node's output.
    • Convert it to a binary buffer.
    • Return it as a binary field named data so downstream nodes (e.g., Google Drive upload) can consume it.
  5. Connect the Code node output to your Google Drive upload node and confirm the binary field name matches what the Drive node expects.

Tip — After uploading, set the file permission to Anyone with the link / Reader (Make Public) in Google Drive. Without this, the thumbnail embed in your blog HTML (<img src="https://drive.google.com/thumbnail?id={id}&sz=w1024">) will return a 403 for public readers.

Step 4 — Fix the Draft Post URL and Post ID Fields

Verify the actual field names returned by the Blogger POST Draft node — two fields from the previous session were guessed incorrectly.
  1. Run the Create Draft node against a real Blogger API call and inspect the raw output JSON.
  2. Confirm field mappings:
    • postId → use {{ $json.id }} (top-level field — this guess was correct).
    • blogId → use {{ $json.blog.id }}.
    • blogUrldo not use {{ $json.url }} for a draft. For a DRAFT post, url only returns the blog homepage, not a permalink.
  3. For the edit link, construct it manually: https://www.blogger.com/blog/post/edit/{{ $json.blog.id }}/{{ $json.id }}

Note — A published post would populate url with the real permalink. Because this workflow saves drafts, the direct edit URL is more useful for review before publishing.

Step 5 — Build the YouTube Metadata Child Workflow

Create T6-Content-Child-YouTube as a 6-node workflow that generates YouTube SEO metadata and writes it to a Google Doc.
  1. Add a When Called by Another Workflow trigger node; define one input field: topic.
  2. Add an AI / Claude node (model: Claude Haiku 4.5) named YouTube SEO:
    • Connect it to a Structured Output Parser.
    • The prompt must demand JSON-only output with fields: title, description, tags, timestamps.
  3. Add a Code node named Format Doc Content to shape the parsed JSON into a readable Google Doc body string.
  4. Add a Google Docs node set to Create; note that the field holding the new document's ID in the output is id — not documentId (a common wrong guess).
  5. Add a second Google Docs node set to Update (Insert Text) to write the formatted content into the newly created document; reference the doc ID with {{ $json.id }}.
  6. Add a final Code node named Build YT Output; prefix all output fields with yt_ (e.g., yt_title, yt_tags, yt_docUrl, processedBy_YouTube) to prevent field collisions when Merging with Blog child output later.

Tip — This child workflow generates text metadata only — it does not create or upload a video. Auto-injecting this metadata into an actual YouTube upload requires a separate step: update the child to also write into the T5-Video-Metadata Sheet, which the upload workflow reads from.

Step 6 — Fix "Model Output Doesn't Fit Required Format" in the Output Parser

If the Structured Output Parser throws this error, tighten the prompt before reaching for advanced fixes.
  1. Edit the YouTube SEO node's system/user prompt to explicitly state: respond with JSON only, no markdown fences, no extra commentary, no trailing text.
  2. Test again — in most cases, this prompt strictness (Fix 1) resolves the error completely.
  3. The Auto-fixing Output Parser (Fix 2) is a more robust fallback that automatically retries malformed outputs. Keep this as a documented option for production hardening but do not implement it now unless Fix 1 fails.

Step 7 — Connect Both Children in the Parent Dispatch Workflow

Update T6-Content-Factory-Dispatch to fan out to both child workflows in parallel and merge their results.
  1. After the Limit node, draw two separate wires — one to Call Child Blog and one to Call Child YouTube.
  2. Set both Execute Sub-workflow nodes to Run once for each item so each topic spawns both children.
  3. Add a Merge node after both children; set its mode to Combine by Position.
  4. Connect both child output wires into the Merge node, then connect Merge to the Mark Done node.
  5. Test with one topic (e.g., "Top 5 amenities at Vinhomes Global Gate") and confirm:
    • Merge outputs exactly 1 item.
    • The item contains both blog fields and yt_-prefixed YouTube fields with no overwrites.

Production tip — The yt_ prefix on all YouTube child output fields is what prevents Merge from overwriting blog fields that share the same name (e.g., both children might output a title). Always prefix child outputs when multiple children feed a single Merge node.

Key Lessons from This Session

  1. DALL-E 2 and DALL-E 3 are permanently gone. Any workflow using those models must migrate to gpt-image-1 — there is no fallback or grace period.
  2. gpt-image-1 returns Base64, not a URL. Replace any "Download Image" node with a Code node that converts data[0].b64_json to a binary field.
  3. Draft Blogger posts do not return a real permalink. Build the edit URL manually from blog.id and id instead of relying on $json.url.
  4. Google Docs Create node returns id, not documentId. Always inspect raw output before writing expressions that reference node fields.
  5. Prefix child output fields to avoid Merge collisions. Use a consistent naming convention like yt_ for all fields from the YouTube child workflow.
  6. Prompt strictness fixes most Output Parser errors. Require JSON-only responses before reaching for the Auto-fixing Output Parser.
  7. Search before you fix, especially for API changes. When an AI tool suggests a fix for an API error, verify current model availability online first.

Conclusion:

In this n8n tutorial, we migrated image generation from the retired DALL-E to gpt-image-1, handled the Base64 response format correctly, fixed draft post field mappings, and built a parallel YouTube metadata child workflow — completing the core Content Factory. The next session focuses on production hardening: error output routing on each Execute Sub-workflow node, switching the parent trigger from Manual to Schedule, and linking an Error Handler workflow. If you have any questions, feel free to leave a comment below. Thank you!

Tags: n8n image generation AI, n8n tutorial, n8n workflow automation, gpt-image-1, n8n HTTP Request node, OpenAI image API, n8n sub-workflow, n8n Merge node

Maybe you are interested!

n8n tutorial - Lesson 21: Sub-Workflows in n8n: Build Modular Automation

n8n tutorial - Lesson 21: Sub-Workflows in n8n: Build Modular Automation

Hi everyone, in this n8n sub workflow tutorial, you'll learn how to split a large automation into reusable child workflows using the Execute Workflow node. This is a core pattern in n8n workflow automation that keeps your projects modular, maintainable, and easy to scale.

How to do:

Step 1 — Understand When to Use Sub-Workflows

Before building anything, know the five situations where the sub-workflow pattern pays off in n8n workflow automation.
  1. Use a sub-workflow when the same logic is called from multiple parent workflows — for example, a "post to Blogger" routine used by both a blog factory and an email pipeline.
  2. Use it when a single workflow exceeds ~15 nodes and becomes hard to read or debug.
  3. Use it when you want to test one processing unit (text formatting, image generation, number math) independently without running the full parent.
  4. Use it when different team members own different stages — each person edits their own child workflow without touching shared logic.
  5. Use it when you need to process each item in a loop through a consistent set of nodes — the parent iterates, the child does the work.

Step 2 — Build Child Workflow A (Text Processor)

The first child workflow, T6-B2-Child-A-TextProcessor, receives a text string, transforms it to uppercase, and returns the result.
  1. Create a new workflow named T6-B2-Child-A-TextProcessor.
  2. Add a When Executed by Another Workflow trigger node. In the Input Schema, define one field: name text, type String.
  3. Add a Set node after the trigger. Configure three output fields:
    • result → expression {{ $json.text.toUpperCase() }}
    • processedAt → expression {{ $now.toISO() }}
    • processedBy → fixed string Child-A-TextProcessor
  4. Save the workflow, then click Test workflow with pinned data {"text": "hello sub workflow"}. Expected output: result: "HELLO SUB WORKFLOW".

Note — Depending on your n8n version, the When Executed by Another Workflow trigger's schema screen may only show Name and Type fields — there is no auto-generated sample value input box. You must pin test data manually to run standalone tests.

Step 3 — Build Child Workflow B (Number Processor)

The second child, T6-B2-Child-B-NumberProcessor, receives a number and returns its square — demonstrating that each child can use a completely different node type.
  1. Create a new workflow named T6-B2-Child-B-NumberProcessor.
  2. Add a When Executed by Another Workflow trigger. Define one schema field: name number, type Number.
  3. Add a Code node with the following JavaScript:
    • const n = items[0].json.number;
    • return [{ json: { squared: n * n, processedAt: new Date().toISOString(), processedBy: "Child-B-NumberProcessor" } }];
  4. Save and test with pinned data {"number": 7}. Expected output: squared: 49.

Step 4 — Build the Parent Dispatch Workflow

The parent workflow, T6-B2-Parent-Dispatch, sends data to both children in parallel and merges the results.
  1. Create a new workflow named T6-B2-Parent-Dispatch.
  2. Add a Manual Trigger node, then a Set node to mock input — set text to hello sub workflow and number to 7.
  3. From the mock input node, create two parallel branches:
    • Branch 1: Execute Workflow node → select T6-B2-Child-A-TextProcessor. Set Run once for all items. Map text{{ $json.text }}.
    • Branch 2: Execute Workflow node → select T6-B2-Child-B-NumberProcessor. Set Run once for all items. Map number{{ $json.number }}.
  4. Add a Merge node connected to both branches. Set Mode to Combine By Position.
  5. Run the parent and verify the merged output contains fields from both children: result, squared, and both processedAt values.

Tip — Use Combine By Position (not Combine By Matching Fields) when your two branches don't share a common key field. Combine By Matching Fields requires a shared identifier — in this demo, there is none.

Step 5 — Know the 7 Gotchas Before Going Further

These are the practical issues you will hit in real n8n sub workflow builds.
  1. Field name collision: If Child A and Child B both output a field called processedAt, the Merge node will overwrite one with the other. Rename fields to be unique per child (e.g., processedAt_A vs processedAt_B).
  2. Timezone offset: $now.toISO() returns local time; new Date().toISOString() in a Code node returns UTC. Be consistent across children to avoid confusion in logs.
  3. Cross-node reference breaks after deleting a node: If your child was duplicated from another workflow and still references a deleted node — e.g., $('Get Topics') — the expression will throw an error. Update every reference to point to the new trigger: $('When Executed by Another Workflow').
  4. Double-equals bug: In the Execute Workflow input mapping, if the field is already in Expression mode, type {{ $json.topic }} — do NOT prefix it with =. Writing ={{ $json.topic }} passes the literal string =Hướng dẫn... as the topic title instead of the actual value.
  5. Schema only validates, it does not inject sample data: The trigger schema defines expected input types. It does not create a test input form — pin data manually for standalone testing.
  6. Run mode matters: Run once for all items calls the child once and passes all items in a batch. Run once for each item calls the child once per item. Use "each item" when the child must process one topic at a time (e.g., the Content Factory).
  7. On Error behavior: Each Execute Workflow node has an On Error setting with three options: Stop Workflow (default — halts everything), Continue (skips the failed item silently), Continue (using error output) (routes the error to a separate output pin for handling). For production, set this to Continue (using error output) so one bad topic doesn't kill the whole batch.

Step 6 — Refactor Content Factory into Parent + Child

Now apply the sub-workflow pattern to a real project: the existing T4-B5-Blog-Batch workflow (14 nodes) is split into a parent dispatcher and a child blog generator.
  1. Design the split:
    • Parent (T6-Content-Factory-Dispatch): reads topics from Google Sheet T4-B5-Blog-Topics, calls the child for each topic, marks the sheet row as done.
    • Child (T6-Content-Child-Blog): receives one topic string, generates HTML blog post, posts draft to Blogger, returns topic / status / postId / blogUrl.
  2. Build the child workflow:
    1. Duplicate T4-B5-Blog-Batch. Rename it T6-Content-Child-Blog.
    2. Delete the Get Topics, Limit, and Mark Done nodes.
    3. Add a When Executed by Another Workflow trigger with schema field topic (type String).
    4. Find every expression that references $('Get Topics') and replace it with $('When Executed by Another Workflow').
    5. Add a final Set node (Build Output) that returns: topic, status, postId (from $json.id of the Blogger POST response), blogUrl (from $json.url).
  3. Build the parent workflow:
    1. Duplicate T4-B5-Blog-Batch. Rename it T6-Content-Factory-Dispatch.
    2. Delete the 11 internal processing nodes, keeping only: Manual TriggerGet TopicsLimitMark Done.
    3. Insert a Call Child Blog (Execute Workflow) node between Limit and Mark Done. Select T6-Content-Child-Blog. Set Run to Run once for each item.
    4. Map the child input: field name topic, value {{ $json.topic }} (no leading =).
    5. In the Mark Done node, set the match condition to topic = {{ $json.topic }} — this uses the topic field returned by the child output, ensuring the correct sheet row is marked regardless of item ordering.

Production tip — Always match Mark Done using a value from the child's output (not the parent's pre-call data). If the child transforms or normalizes the topic string, matching against the child's returned topic field prevents row-pairing mismatches in your sheet.

Step 7 — Test the Full Content Factory and Fix Errors

Run the parent with one topic in the sheet and work through the three errors that appear in a typical first run.
  1. Error 1 — DALL-E response_format known issue:
    • The OpenAI DALL-E node throws an error related to the response_format parameter on certain n8n versions.
    • Workaround: disable the DALL-E node and its 3 downstream image-handling nodes in T6-Content-Child-Blog. The blog post will be created without an image until this is fixed in Session 22.
  2. Error 2 — Double-equals in topic title:
    • Symptom: the blog post title appears as =Hướng dẫn... (the raw expression string, not the evaluated value).
    • Fix: open the Call Child Blog node, find the topic input field, confirm it is already in Expression mode, and change the value from ={{ $json.topic }} to {{ $json.topic }}.
  3. Error 3 — Broken cross-node reference:
    • Symptom: child workflow throws Referenced node does not exist: Get Topics.
    • Fix: search the child for every expression containing $('Get Topics') and replace with $('When Executed by Another Workflow').
  4. After all three fixes, run the parent again. Expected results:
    • Blogger draft created with clean title ✅
    • Google Sheet row marked as done ✅
    • No expression errors ✅

Note — The DALL-E issue is a known bug in specific n8n versions. The fix — replacing the OpenAI node with an HTTP Request node calling images/generations directly without the response_format parameter — is scheduled for the next session.

Key Lessons from This Session

  1. Always test each child workflow standalone before connecting to the parent. Pin sample data to the trigger and verify output before wiring the Execute Workflow node in the parent.
  2. Use Combine By Position when branches share no common key field. Combine By Matching Fields requires a shared identifier — if none exists, the merge will silently drop rows.
  3. The double-equals bug (={{ }}) passes a literal string, not an expression. If an input field is already in Expression mode, write {{ $json.field }} without the leading =.
  4. Cross-node references break when the referenced node is deleted. After duplicating a workflow and removing nodes, audit every expression for references to deleted node names.
  5. Match Mark Done using the child's output topic, not the parent's pre-call data. This prevents sheet row mismatches when items are processed out of order.
  6. Set Execute Workflow error handling to "Continue (using error output)" in production. This routes failed items to a separate pin instead of stopping the entire batch.
  7. Run once for each item vs. run once for all items controls batching behavior. Use "each item" when the child must handle one record at a time with its own context.

Conclusion:

In this n8n sub workflow tutorial, you built a full three-workflow demo — two standalone child processors and a parent dispatcher — then applied the same pattern to a real Content Factory by refactoring a 14-node monolith into a clean parent-plus-child architecture. The sub-workflow pattern is the foundation for orchestrating multi-format content pipelines in n8n tutorial series like this one, and every production technique covered here (error routing, expression mode awareness, cross-node reference auditing) will carry forward into more advanced n8n workflow automation builds. Next session covers re-enabling the DALL-E image branch with a direct HTTP Request workaround, verifying postId and blogUrl output values, and expanding the factory to dispatch to YouTube and Email child workflows.

If you have any questions, feel free to leave a comment below. Thank you!

Tags: n8n sub workflow tutorial, n8n tutorial, n8n workflow automation, Execute Workflow node, modular automation, n8n Content Factory, n8n beginner to advanced, workflow design patterns

Maybe you are interested!

Copyright © 2016 QTitHow All Rights Reserved