This tutorial shows you how to build an automated 3-email follow-up sequence in n8n that handles all of that for you. The workflow checks your contacts list daily, sends the right email to each person based on timing, and stops automatically when someone replies. (Workflow included)
Real use cases: People who downloads your ebook, new lead onboarding, anything where you need to stay in touch without manually tracking 50+ conversations.
Why n8n instead of Mailchimp or Hubspot?
FREE. Unlimited contacts. You own the data. No monthly fees that scale with your list size. You need a paid subscription in HubSpot to enable automated nurturing email sequences. n8n costs $0 if you self-host, or $20/month for unlimited everything on n8n cloud.
I’ll show you how to build the simplest version first, a 3 email sequence that actually works. then I’ll explain how to add reply detection and scaling things up.
The Email Sequence Strategy (Before You Build)
Most people jump straight into building workflows and wonder why their sequence feels wrong. The timing is off, or they send too many emails, or not enough.
You should need to decide first before you add your first node.
How Many Emails Should You Send?
Three emails is the sweet spot for most follow-up sequences based on my marketing experience.
Email #1: Initial contact or First feedback email(reminds them who you are, offer value)
Email #2: Follow-up (adds context, share resources or case study)
Email #3: Final check-in (gentle nudge, easy opt-out)
Why nore more? Industry data shows 80% of responses come from the first three emails. Email 4 and 5 mostly annoy people who’ve already decided not to respond.
Long-term nurturing sequences (educational content over weeks/months). But that’s a different workflow. For follow-ups after a meeting or demo, stick with three or anyways you can try out your own phase, maybe more than 3 could work for you too. Alright, let’s stick to the plan.
The Workflow Structure (High Level Overview)
Before configuring individual nodes, understand how the pieces going to connect. Let’s start the plan.
The objective: I want to send emails for those who downloaded my marketing e-book
Right now, I’ve their details such as email, full name.
Your email sequence workflows has 6 core nodes
Manual trigger (for testing) / Schedule trigger
Google Sheets (Read)
Switch Node
Set Node
AI Agent (Gemini Chat Model)
Code Node
Set Node
Gmail Node
Switch Node
Google Sheets (Update)
Building the Workflow
This is where everything comes together. By the end of this section you’ll have a working workflow that reads your contact list, decide which follow-up email to send, generates it with AI, send it via Gmail, and updates your sheet automatically.
Add a Google Sheets node and connect it to the trigger.
Configurations
Resource: Sheet data
Operation: Read (Get Rows)
Document: Select your email Follow-up sequence sheet
Sheet: Sheet1 (for me)
When you execute this node you should see all your user details flowing through separate items.
One thing to check: Make sure every row in your sheet has a valid email address in the Users Email column. A single empty row will crash the Gmail node later. If you have blank rows, delete them now.
Step 3: The Switch Node
This is the brain of the workflow. The Switch node looks at each column and decides which email they need next.
Well, basically, you need to create 3 separate Routing Rules for separate follow ups.
Logics
If Follow Up is false means we have to send them the follow-up email
If Follow Up is true means we have sent them the follow-up email
What you’ve to do is, for 3 routes you have to assign those follow ups column appropriately.
Step 4: Set Node (One Per Route)
Each Switch route needs to know which email it’s sending. Add a SET node on each of 3 routes.
Make sure add a SET node to it’s Switch follow-up route
Now add these configurations to SET nodes individually
This is for Follow Up #1
followUpNumber – 1
followUpGoal – Confirm they received the ebook and invite questions
columnsToUpdate – Follow Up #1
This is for Follow Up #2
followUpNumber – 2
followUpGoal – Check if they started reading, share one key insight
columnsToUpdate – Follow Up #2
This is for Follow Up #3
followUpNumber – 3
followUpGoal – Ask for one-line feedback on the ebook
columnsToUpdate – Follow Up #3
Make sure to know this, you can add whatever here on the followUpGoal for all these 3 SET nodes. It’s all about giving an additional contexts. You maybe have a different goal, add that goal in one liner.
Step 5: The AI Agent
Add an AI Agent node. Connect Google Gemini Chat Model to it’s Chat Model input.
Now add this system prompt to AI Agent node.
You are an email writer for Shajid from The Owl Logic (theowllogic.com),
a blog focused on n8n automation and workflow development.
Shajid recently launched an ebook about n8n automation. The recipient
downloaded this ebook and you are writing a follow-up email on his behalf.
## Recipient Details:
- Name: {{ $('Get Customer Data').item.json.Name }}
- Company: {{ $('Get Customer Data').item.json.Company }}
## Which Email to Write:
- Follow Up Number: {{ $json['followUpNumber:'] }}
- Goal: {{$json.followUpGoal}}
- Column to Update: {{$json.columnToUpdate}}
## Email Instructions Based on Follow Up Number:
If followUpNumber = 1:
- Subject: "Your n8n Automation Ebook is Ready, {{ $('Get Customer Data').item.json.Name }}"
- Goal: Confirm delivery, tell them what's inside, invite questions
- Tone: Warm welcome, excited but not salesy
If followUpNumber = 2:
- Subject: "Did you get a chance to read it, {{ $('Get Customer Data').item.json.Name }}?"
- Goal: Check if they started reading, share ONE specific insight
from the ebook to spark curiosity, re-share download link
- Tone: Casual check-in, friendly nudge
If followUpNumber = 3:
- Subject: "Quick question about the ebook, {{ $('Get Customer Data').item.json.Name }}"
- Goal: Ask for one-line feedback, mention you're building more
resources and their input shapes what gets built next
- Tone: Direct, honest, respectful of their time
## Rules for ALL emails:
- Max 150 words
- Write in first person as Shajid
- Conversational, not corporate
- No emojis
- No marketing language or hype words
- No phrases like "I hope this email finds you well"
- Sound like a real person writing to one person
- End with: "- Shajid"
## Output Format:
Return ONLY this JSON structure, nothing else:
{
"subject": "email subject here",
"body": "email body here",
"columnToUpdate": "{{$json.columnToUpdate}}"
}
You can simply have it in your own way though based on your requirement, for this tutorial, try to copy me until you succeed, after that you can be able to make your own follow-up sequence.
We are asking Gemini to write follow-up email {{ $json.columnToUpdate }} for: which means we are passing down the follow up number whether 1,2,3 – so basically we
Watch this video below for understanding them perfectly.
Step 6: The Code Node to Parse JSON
AI Agent returns everything as one string. This Code node breaks it into a separate fields.
const results = [];
for (const item of $input.all()) {
// Safety check - skip items without output
if (!item.json.output) {
results.push({ json: { ...item.json, error: 'No output from AI Agent' } });
continue;
}
try {
const raw = item.json.output;
// Clean markdown code blocks if present
const cleaned = raw
.replace(/```json\n?/g, '')
.replace(/```\n?/g, '')
.trim();
const parsed = JSON.parse(cleaned);
results.push({
json: {
...item.json,
subject: parsed.subject || 'Follow up from Shajid',
body: parsed.body || '',
columnToUpdate: parsed.columnToUpdate || item.json.columnToUpdate
}
});
} catch (e) {
// If JSON parse fails, log which item failed and why
results.push({
json: {
...item.json,
error: `Parse failed: ${e.message}`,
rawOutput: item.json.output
}
});
}
}
return results;
Step 7: Adding a SET Node (Again)
Now we need to separate what code node gives us, perhaps you don’t need this node, but I added here for easy to access data. I don’t want run into any confusion later though.
Map the data to SET node from the Code node.
Add the body
Add the subject
Add the Users email from Sheet directly.
So it should come like this all the information.
Step 8: Gmail Node
Add a Gmail Node (to send message)
Configuration
To: {{ $json['Users Email'] }}
Subject: {{ $json.subject }}
Email Type: HTML
Message: {{ $json.body }}
Step 9: Switch Node to Update Google Sheet
Alright, now we have to connect another Switch Node.
You have to connect the previous Switch nodes state to this new Switch node. Simply drag and drop the Follow Up #1 and create a Routing rule.
Make sure to create 3 separate routing rules for Follow Ups, like how I did.
Step 10: Add Google Sheets to Per Route
Now we have 3 different routes for follow-ups, and we need to attach 3 different Google Sheet (update) nodes to the each route.
Configuration
Operation: Update Row
From List: Your Document
Sheet: Sheet 1 (mine)
Mapping Column Mode: Map Each Column Manually
Columns to Match On: Users Email
Make sure to understand this, for each route has follow up.
Route 1: Follow Up #1: You need to Update the Google Sheet Follow Up #1 Column to TRUE, and keep others empty
Route 2: Follow Up #2: You need to Update the Google Sheet Follow Up #2 Column to TRUE, and keep others empty
Route 3: Follow Up #3: You need to Update the Google Sheet Follow Up #3 Column to TRUE, and keep others empty
Final Execution Email Follow-Up
So literally before you add your contact lists, you work with one email for testing, whether everything works perfectly, including the formatting, After that gradually test with 5, 10, 20, 30 and once passed without any failure. You can move on with your lists.
Frequently Asked Questions
How do I add delays between follow-up emails?
Don’t use Wait Nodes for multi-days delays. They block your entire workflow – if Contact 1 hits a 3-day wait, every other contact get stuck behind it.
The correct approach: Scheduled trigger + date in your Switch conditions.
Your workflow runs daily and asks “has enough time passed?” instead of sitting open for 3 days.
Add a Last_Email_Date column to your sheet. Each Update node writes today’s date after sending. The Switch condition checks how many days have passed before sending the next email.
Can I Use SMTP instead of Gmail?
Yes. Replace the Gmail node with n8n’s Send Email node and plug your SMTP credentials.
Good free options SendGrid (100/day), Brevo (300/day), Mailgun(1,000/day trial)
Gmail works fine for testing and small lists. Switch to SMTP when you hit over 200+ contacts per day.
Can I Connect MailChimp or ActiveCampaign?
Yes. Instead of the Gmail node, use n8n’s Mailchimp or ActiveCampaign node to add contacts directly into their automations.
That said, if you’re already building this in n8n, you don’t need them. n8n + Gmail handles everything for almost for free.
How do I stop the sequence if someone replies?
That requires a second workflow running alongside with this one, The second workflow.
Monitor your Gmail inbox using an Email trigger (IMAP) node
Check if the sender’s email matches anyone in your Google Sheet
If Yes, update their statuses to mark sequence complete.
Well, make sure to subscribe to our email newsletter for more information regarding this workflow. You can directly talk to me regarding any issue with this workflow or to make this workflow great.
My Final Thoughts
You just built an AI-powered follow-up email sequence from scratch in n8n.
No mailchimp subscriptions. No hubspot invoices, Just n8n. Pure play.
What’s next? If you want this running fully or autopilot without manual executing it, switch to a Schedule trigger, with data math as I mentioned above. Workflow handles timing on it’s own.
Why most beginners struggle with n8n (And, It’s not what you think).
You’ve finished the First Hello World Workflow. You understand what an HTTP Request node does. You know how to connect nodes together. But when you open n8n to automate something real – like monitoring Reddit mentions of your product, you literally freeze.
The canvas is blank. You know the tools exist, but you have no idea where to start.
Technical knowledge isn’t your problem. Workflow decomposition is.
Most n8n tutorials teach you what nodes do. They show you how to configure an HTTP Request or parse JSON. But they skip the most important skill – breaking down a problem into automation-friendly steps before you touch a single node.
I learned this the hard way after watching dozens of beginners (myself included) build workflows that technically work but are impossible to maintain. They skip planning, jump straight to adding nodes, and end up with spaghetti logic that breaks the moment anything changes.
This guide teaches you the thinking process that comes before the clicking. Once you understand how to decompose problems into workflow patterns, building in n8n becomes surprisingly straightforward.
The Workflow Thinking Framework
Before you add your first node, answer four questions. These questions force you to think like a workflow instead of like a human performing tasks manually.
Question 1: What’s the Desired Outcome?
Start with the end result, not the process to get there.
Bad example: “I want to scrape Reddit.” Good example: “I want to receive a Slack notification when a new post in r/n8n mentions ‘workflow automation’ with 50+ upvotes.”
Can you see the difference? The good example is specific, measurable, and describes the actual value you’re creating. The bad example describes a technical action without context.
Write your desired outcome as: “I want to [specific result] when [trigger condition] so that [business value].”
This forces clarity. If you can’t fill in all three parts, your problem isn’t well-defined yet.
Question 2: What Triggers the Workflow?
Workflows need a starting point. In n8n, this is your trigger node, and choosing the wrong type creates problems later.
Ask yourself: Does this workflow start based on time, an external event, or a manual action?
Time-based triggers run on a schedule:
“Every Monday at 9am, generate a weekly report”
“Every 4 hours, check inventory levels”
Use the Schedule Trigger node
Event-based triggers respond to something happening:
Most beginners default to Manual triggers because they’re testing. That’s fine for development, but switch to the real trigger type before deploying. A workflow that should respond to events won’t work if it requires manual clicks.
Question 3: What Data Transformations Are Needed?
Every workflow follows the same pattern: Input → Transform → Output.
Input is the raw data from your trigger. It’s usually messy, contains extra fields you don’t need, and isn’t in the format the next step expects.
Transform is where you clean, filter, format, and enrich that data.
Output is what the next step (or final destination) needs.
Let’s use a real example. You’re pulling posts from Reddit’s API:
Input: 50 Reddit posts in JSON format, each with 20+ fields (author, created_utc, score, title, selftext, url, subreddit, etc.)
Transform:
Filter to only posts with score > 100
Extract just title and url
Format as a readable message
Output: A clean list of high-scoring posts ready for your Slack node
This thinking process maps directly to n8n nodes. The transformation step might need multiple nodes, a Filter node to remove low-scoring posts, a Set node to extract specific fields, and a Code node if you need custom formatting.
Beginners skip this question. Then their workflow runs fine for a week and suddenly breaks.
Think through failure modes before building:
What if the external API is down? Your workflow should handle timeouts gracefully instead of failing silently. Maybe you send yourself an alert, or retry after 5 minutes.
What if no data matches your filters? If you’re filtering Reddit posts for those with 100+ upvotes and none exist today, should your workflow fail? Or should it complete successfully with zero items?
What if data format changes? APIs add new fields, remove old ones, or change data types. Your workflow should validate critical fields exist before using them.
I’ve seen workflows break because someone assumed an API always returns an array. One day it returned null, and the workflow crashed trying to loop over nothing.
Add error handling early. It’s easier than debugging production failures at 2am.
Three Mental Models for Common Automation Patterns
Most workflows follow recognizable patterns. Learn to identify these, and you’ll design better automations faster.
Pattern 1: The Data Pipeline (Fetch → Transform → Deliver)
When to use it: You’re moving data from one place to another with minimal logic in between.
This is the simplest pattern. Data flows in one direction through a series of transformations, then lands somewhere useful.
Example scenario: Send a daily weather report to Slack every morning at 8am.
The workflow structure:
Schedule Trigger – Runs at 8:00am daily
HTTP Request – Fetch weather data from OpenWeather API
Set node – Extract temperature and conditions from the JSON response
Code node – Format a readable message like “Today in San Francisco: 72°F, partly cloudy”
Slack node – Send the formatted message to #general
No branching logic, no loops, no complex decision-making. Data enters at the top, gets transformed step by step, and exits at the bottom.
This pattern works well because it’s linear and predictable. When something breaks, you know exactly where to look—the step that’s failing.
Pattern 2: The Decision Tree (If This, Then That)
When to use it: Different inputs require different actions.
Real-world problems rarely have one-size-fits-all solutions. Support emails need different responses based on urgency. Sales leads need routing based on company size. File uploads need different processing based on file type.
Example scenario: Triage support emails and route them based on urgency.
Path A: Email contains “urgent” or “down” → High priority
Path B: Email contains “bug” or “error” → Medium priority
Path C: Everything else → Low priority
Different actions per path:
High priority → Send Slack alert to on-call engineer
Medium priority → Create ticket in system with priority flag
Low priority → Send auto-reply, create normal ticket
The IF node is your friend here. Each branch handles its specific case independently. This is much clearer than trying to build one giant node that handles all scenarios.
When to use it: You need to perform the same action on multiple items, but each item needs individual processing.
This is where beginners often get stuck. They think “I have 100 contacts, I need to send 100 emails” and try to build a workflow that handles all 100 at once. That’s not how n8n works.
n8n processes items in batches automatically, but understanding how loops work prevents confusion.
Example scenario: Send personalized follow-up emails to 100 conference attendees.
The workflow structure:
Schedule Trigger – Run once (manual or scheduled)
Google Sheets node – Fetch all attendee data (name, email, company, session attended)
Loop Over Items – n8n handles this automatically,
For each attendee, the workflow processes them individually
The Email node receives one attendee at a time
Set node – Create personalized variables for each attendee
Code node – Build custom email body: “Hi {{name}}, thanks for attending {{session}} at our conference…”
Send Email node – Send to each attendee individually
Wait node – Pause 2 seconds between emails to respect rate limits
The critical insight: n8n automatically loops over items from previous nodes. You don’t need to write a for-loop yourself. Each node processes all items it receives, one at a time.
But you do need to handle rate limits. If you’re sending 100 emails, most email services will throttle or block you if you send them all instantly. That’s where the Wait node saves you—or better yet, learn about handling API rate limits in n8n.
Practical Exercise: Design Your First Workflow (Before Opening n8n)
Here’s the mistake I see constantly: beginners open n8n, add a few nodes, realize they don’t know what comes next, and start over. Then they do it again. And again.
Stop doing that. Plan on paper first.
Use this pre-workflow checklist before touching your mouse:
1. Write the Problem in One Sentence
Use this template: “I want to [outcome] when [trigger] so that [benefit].”
Don’t cheat and make it vague. Be painfully specific.
Example: “I want to save new videos from the Fireship YouTube channel to my Notion workspace when they’re published so that I have a curated learning library.“
2. Map the Data Flow
Answer three questions:
What data comes in?
RSS feed from YouTube containing video title, URL, publish date, description
What transformations happen?
Filter out YouTube Shorts (keep only videos longer than 60 seconds)
Extract just the title, URL, and publish date
Format publish date from ISO 8601 to readable format
What data goes out?
Clean database entry in Notion with Title, URL, Date Added fields
This step forces you to think about data types and formats. An RSS feed gives you different data than a direct API call. Notion expects data in a specific format. Understanding this before building saves hours of debugging.
3. Identify Your Pattern
Which pattern does this match?
Data Pipeline – Fetch → Transform → Deliver (linear flow, no complex logic)
Decision Tree – Different actions based on conditions
Batch Processor – Same action on multiple items
Our YouTube-to-Notion example is a Data Pipeline with filtering.
4. List Required Nodes (But Don’t Open n8n Yet!)
Based on your pattern and data flow, write down which nodes you’ll need:
RSS Feed Trigger (fetch YouTube feed)
Filter node (remove Shorts)
Set node (extract specific fields)
Code node (format date – optional, could use expressions)
Notion node (create database entry)
Notice I wrote “optional” for the Code node. This is thinking through alternatives. n8n expressions might handle date formatting without custom code.
5. Anticipate One Failure Point
What’s the most likely thing to break?
In our example: YouTube’s RSS feed could be down, return empty results, or change their data structure.
How would we handle it? Add an IF node after the RSS Trigger. If the feed returns zero items or errors, send yourself a Slack notification instead of trying to create empty Notion entries.
Now Open n8n and Build It
Here’s what will happen: You’ll build the workflow in 10 minutes instead of an hour because you’ve already solved the hard problems. You’re translating a plan into nodes, not designing while building.
When you get stuck, you’ll know exactly where the problem is. Your plan said “filter out Shorts,” so if that’s not working, you know the Filter node configuration is wrong—not your entire approach.
Common Thinking Traps (And How to Avoid Them)
Trap 1: “I’ll Figure It Out As I Go”
This leads to workflow spaghetti. You add nodes reactively, realize you need something earlier, insert nodes in the middle, and end up with a tangled mess.
Solution: Spend 5 minutes planning before adding your first node. Use the pre-workflow checklist above. I promise you’ll save 30 minutes of confused clicking.
Trap 2: “This Needs to Be Perfect”
Analysis paralysis kills momentum. You research every possible approach, read documentation for hours, and never build anything.
Solution: Build the MVP workflow first. Get it working with the simplest possible approach, even if it’s not elegant. You can always optimize later. A working workflow that’s ugly is infinitely more valuable than a perfect workflow that doesn’t exist.
Trap 3: “I Need a Node for Everything”
n8n has hundreds of nodes, but you don’t need to master them all. Sometimes a Code node with 5 lines of JavaScript is simpler than finding the perfect specialized node.
Solution: Use Code nodes for simple transformations. String manipulation, date formatting, basic math—these are often clearer as explicit code than as complex expressions or chains of Set nodes.
Trap 4: “It Works, Ship It!”
Your workflow runs successfully once, so you deploy it to production. Then it breaks in the first week because you didn’t handle errors.
Solution: Add error handling before deploying. Test failure scenarios intentionally:
What if the API returns an error?
What if you get zero results?
What if the data format is unexpected?
Workflows that handle errors gracefully are the difference between a helpful automation and a maintenance nightmare.
Next Steps: Apply This to Real Automation
You now have a mental framework for designing workflows. Don’t just read this and move on and apply it.
Your assignment:
Pick one boring task you do manually every day or week. It doesn’t need to be complex. In fact, start simple.
Examples:
Save attachments from specific emails to a folder
Post your new blog articles to Twitter automatically
Track product mentions on Reddit
Generate a weekly summary of Slack messages
Apply the 4-Question Framework:
What’s the desired outcome? (Be specific)
What triggers the workflow? (Time, event, or manual)
What transformations are needed? (Input → Transform → Output)
What could go wrong? (Anticipate one failure)
Choose your pattern (Pipeline, Decision Tree, or Batch Processor).
The difference between beginners who struggle and those who succeed isn’t technical skill. It’s this: successful automation builders think through the workflow before they build the workflow.
You just learned how to think like a workflow. Now go automate something.
Every article I publish goes through the same tedious steps – generate an outline, run grammar checks, format for WordPress CMS, create social posts (for repurposing). I used to do each step manually, switching between tabs (Now split in chrome tabs), copying and pasting, checking boxes off a list. Guess how many hours it would take?
For me, It takes about 14 hours repetitive tasks, then I switched my specific skills and tasks to no-code automation platform (n8n).
Now it handles the entire pipeline. I focus on writing. The boring stuff happens automatically while keeping me as a human-in-loop.
What’s human in loop?The automation runs, but I review the output before it takes final action – or I check the results afterward to catch anything off. Either way, I stay in control without doing the repetitive work myself.
What “Boring Stuffs” Can You Actually Automate?
Before we get into “how”, here’s what n8n can take off your plate.
Data entry and spreadsheet syncing – If you receive any new form submission or order, you can easily add it to Google sheets or to your database. Customer data changes in CRM? sync it everywhere instantly.
Email management – Auto reply to common questions, sort incoming emails by sender or keyword, forward specific messages to team members, from email to slack or maybe WhatsApp.
File organization – Rename file based on patterns, move upload to dated folders, back up important documents to cloud storage like Google drive or dropbox.
Notification and reminders – Get Slack alerts when something important happens, notify team about your deadlines.
Social media posting – Schedule posts across platforms, auto-share new blog content, post updates when product launch.
Report generation – Pull data from multiple sources (e.g. database, sheets), format it, send it to stakeholders on a schedule
Lead and CRM updates – Capture leads from forms, or emails, enrich the contact data, update deal stages or buckets automatically.
If you find yourself doing the same task more than twice a week, It’s probably a sign that you need to automate.
Why n8n Instead of Python or Zapier?
“Automate the Boring Stuff with Python” is an excellent book. But it assumes you want to learn programming. If you just want to stop wasting time on repetitive tasks, writing, and maintaining Python scripts might be overkill.
The Python Approach: Write code, debug it, schedule it with cron, maintain it when APIs change, fix it when something breaks. I agree It’s powerful, but requires ongoing technical investment.
The Zapier/Make Approach: Easy drag-and-drop setup, works great for simple tasks, but cost scale quickly – $20 / $50 (monthly) for serious usage – and you’re limited to what they’ve pre-built. (and, yet, they have most of the integrations unless you need a custom one for your business needs)
The n8n Approach: Visual workflow builder like Zapier and Make, but you can self-host the instance locally for free. Write custom nodes when you can’t do something with the visual tools.
50 “Boring Tasks” You Can Automate Today
These are real automations – some I use daily, other I’ve built for my friends or seen in the automation community.
Content & Publishing
Generate article outlines from a list of keywords
Run grammar and spell checks on drafts automatically
Format and publish WordPress posts from Google Docs
Create social media posts when a new blog post goes live
Scrape communities for content ideas and dump them into a spreadsheet (Educational only)
Pull YouTube video transcripts and save to Google Docs
Auto-create featured images using templated designs
Track content performance weekly and compile into a report
Send draft review reminders to editors after 48 hours
Auto-translate posts for multilingual sites
Update internal links across old posts when new content publishes
Email & Communication
Sort incoming emails into folders by sender or keyword
Auto-reply to common questions with templated response
Send follow-up emails 3 days after no-response
Forward invoices to your accountant automatically
Unsubscribe reminders – flag newsletters you never open
Merge data from multiple sheets into one master sheet
Clean up duplicate entries automatically
Pull analytics into a weekly report spreadsheet
Convert CSV uploads to formatted Google Sheets automatically
Validate data entries and flag rows with missing fields
Sync Stripe payments to a revenue track spreadsheet
Auto-calculate monthly totals and append to summary sheet
Pull exchange rates daily and update pricing sheets
Cross-reference two sheets and highlight mismatches
Export database tables to spreadsheet on a schedule
Track affiliate commission from multiple platform in one sheet
Files & Backups
Back up important folders to Google Drive every night
Rename and organize downloaded files by date
Convert uploaded images to webp format automatically
Archive old files to cold storage after 90 days
Notifications and Reminders
Get Slack alerts when a client pays an invoice
Birthday and contract renewal reminders
Notify your team when inventory drops below a threshold
Alert you when a competitor publishes a new content
Client & Project Management
Create Trello/Asana tasks from form submission
Send onboarding emails when a new client signs up
Update project status across multiple tools simultaneously
Generate and send invoices on schedule
Track billable hours from Toggl/Clockify to spreadsheet automatically
Notify clients automatically when their deliverable is ready
Archive completed projects to cold storage after 30 days
Creating meeting notes template in Notion when calendar event starts
Alert you when a client hasn’t responded in 7+ days
Send project kickoff checklist to team when contract is signed
WordPress Specific
Monitor uptime and get alerts when your site goes down
Auto-post new WooCommerce products to social media
Sync WooCommerce orders to Google Sheets or Airtable
Your First Automation Takes 5 Minutes
I won’t walk through a full tutorial here – that’s what the Hello World Workflow Guide is for. But understanding the pattern helps you see how simple this actually is.
Every n8n automation follows the same structure
Trigger > Action > (Optional: More Actions)
The trigger decides when your workflow runs. New email arrives. Form gets submitted. Webhook receives data. You pick the event that should kick things off.
The action decides what happens when triggered. Add a row to Google Sheets. Send a Slack message. Update your CRM.
Between trigger and action, you map the data. “Take the sender’s email from Gmail and put it in Column A”. n8n shows you the actual data structure when you click on any node’s output – you can drag and drop fields instead of writing expressions manually.
If you’ve never touched n8n, you can start with hello world workflow. You’ll have something running in under 10 minutes.
Start With One
Don’t try to automate everything at once. Pick the one task from this list that annoys you most. Build that workflow. Watch it run for a week. or maybe, you have a custom workflow in mind, just try to connect the dots and watch it run for a week.
Once you see n8n handling something you used to do manually, you’ll spot the next opportunity yourself.
I’ve been using n8n for over a year now. Built 50+ workflows. Taught it to business owners. Even wrote dozens of contents about n8n here it se as well on The Owl Logic.
But here’s what I tell people who ask if they should use n8n, “Maybe not”
n8n is undeniably powerful. But it’s not for everyone. Some teams need simpler tools. Others need different pricing models. some want easier onboarding for non-technical staffs like marketers, business managers.
This guide helps you figure out if n8n alternatives make more sense for your situation. i’m not here to bash n8n (you can see from my other articles I genuinely like it). I’m here to give you an honest comparison so you pick the right tool.
Why Teams Looks for n8n Alternatives (Based on Surveys)
Let’s have a small talk about why people actually switch away from n8n.
Steep Learning Curve for Non-Technical Teams
n8n assumes you understand JSON, API requests, and data structures. The code node expects JavaScript or Python. Expressions use syntax that feels alien if you’ve never programmed before.
I saw a post marketing manager spent ~40 minutes trying to format a data. Not because she is slow, n8n just doesn’t hand-hold you through these things.
Limited Pre-Build Integrations
n8n has 400-1200 nodes. Zapier has 8000+, Make has 3000+
The HTTP request node lets you connect to any API, but that required reading documentation, understanding authentication, and debugging requests. Perfectly fine for developers.
Self Hosting Complexity
Self hosting n8n means managing docker containers, database backups, SSL certificates, and infrastructure scaling.
I’ve seen teams abandon self-hosting after three months because managing it consumed more time than the workflows saved. The cloud version removes this complexity but costs significantly more.
Pricing Models Doesn’t Fit for All Use Cases
n8n charges per workflow execution. Make charges per operations (each node action)
I’ve seen $20/month n8n bill balloon to $500 because a workflow with 15 nodes ran every 5 minutes. That same automation would cost $89/moth on Make’s operation pricing.
How to Choose the Right n8n Alternative
Don’t just pick the most popular tool sake of my friend referred me this, Make sure to match the platform to your actual needs.
Your Situation
Look For
Skip Tools That
Non-technical team building automation
Visual no-code builder, 2000+ pre-build nodes, minima setup
Require coding knowledge, self-hosting or API configuration
Zapier – Best for speed and breadth of integration
Activepieces – Best for open-source AI-native automation
Gumloop – Best for AI-powered workflows for non-developers
Pipedream – Best for developer-first serverless workflows
Make (formerly Integromat): Best for Visual Complexity Without Code
Make is n8n’s closest competitor. Visual workflow builder, powerful data manipulation, serious depth. But easier for non-developers
Best for
Marketing teams building multi-step campaigns or Ad campaigns.
Operation teams connecting SaaS tools without coding
Automations with complex branching logic
Notable Strengths
3,000+ integrations covering mainstream and niche apps
Visual builder shows entire workflow at once
Built-in function for data transformation without writing code
Better onboarding for beginners
Limitations
Operation-based pricing gets expensive for workflows with many steps
No Self-hosting option (cloud only)
Custom JavaScript code requires Enterprise plan
Pricing: Free plan with 1,000 operations/month. Paid plans start at $9/month.
vs n8n: More integrations and easier for non-technical users, but less control and significantly more expensive at scale. (I mean massive scalelike 100k operation)
Zapier: Best for Speed and Breadth of integrations
Zapier invented the automation category. still the easiest to use. Still the most expensive at scale
Best for
Teams that need to automate fast without learning curve
Connecting mainstream business apps (everything integrated with zapier)
Simple to medium complexity workflows
Notable Strengths
8,000+ integrations – If an app exists, Zapier probably connects to it
Simplest interface in the marketing (linear trigger > action > action)
AI features for building workflows from descriptions
Extensive template library for common use cases
Best documentation and support
Limitations
Most expensive option by far (a workflow costing $20/month on n8n might cost $200/month on zapier)
Limited customization compared to n8n or make
Code steps exist but are restrictive (timeouts, limited libraries)
Multi-step conditional logic feels clunky
Pricing: Free plan with 100 tasks/month. Paid plans start at $20/month but scale quickly with usage.
vs n8n: Easiest to use with most integrations, but 5-10x more expensive at scale and limited customization. Use Zapier when speed matters more than the cost and you need something working today. Use n8n when you have a developer resources and want control.
Activepieces: Best for Opensource AI-Native Automation
Activepieces is what n8n could have been. True open-source license (not n8n’s Sustainable Use License). Built for AI from the ground up.
Best for
Teams building AI agents and LLM workflows
Developers who want truly open-source (not fair-code)
Organizations needing Model Context Protocol (MCP) support
Notable Strengths
594+ pieces (nodes) with deep AI integration
Supports MCP – 400+ MCP servers for AI agents
True Apache 2.0 license (fork it, embed it, do whatever you want)
AI pieces are automatically available as tools for LLMs
Free self-hosted version with no execution limits
Generous cloud free tier
Limitations
Smaller community than n8n (fewer tutorials, less help)
Integration library smaller than Make or Zapier
Less mature – newer platform means occasional rough edges
Documentation not as comprehensive as established tools
Pricing: Free self-hosted. Cloud free tier with 1,000 tasks/month. Pro plan $100/month.
vs n8n: Similar flexibility and self-hosting, stronger AI focus, truly open-source license. Choose Activepieces if you’re building AI-first workflows or need a genuinely open license. Choose n8n for more integrations and a larger community.
Gumloop: Best for AI-Powered Workflows for Non-Developers
Gumloop built automation for the AI era. Natural language workflow creation, built-in LLM access, AI debugging assistant.
Best for
Non-developers building AI workflows
Content generation and enrichment pipelines
Teams that want AI capabilities without API complexity
Key Strengths
Describe workflows in plain English – Gumloop builds them
Built-in premium LLM access (Claude, GPT-4, etc.)
Gummie assistant debugs workflows and suggests fixes
Visual canvas similar to n8n but optimized for AI operations
Fastest time-to-value for AI-powered automation
Limitations
Less suitable for traditional automation (database ops, email sending)
Smaller integration library for non-AI tasks
Newer platform with evolving feature set
More expensive than self-hosted n8n for high volume
Pricing: Free plan available. Paid plans start around $50/month.
vs n8n: Much easier AI integration without coding, but less general-purpose. Use Gumloop if your primary need is AI workflows (content, enrichment, agents). Use n8n if you need flexibility across AI and traditional automation.
Pipedream: Best for Developer-First Serverless Workflows
Pipedream is for developers who think visually but code when they need to. Event-driven, serverless, multiple languages.
Best for
Developers building webhook-based integrations
Teams comfortable with Node.js, Python, Go
Serverless architecture fans
Notable Strengths
Code-first approach with visual workflow builder
Event-driven architecture (perfect for real-time triggers)
Write code in Node.js, Python, Go, or Bash
Generous free tier (10,000 invocations/month)
Native support for npm packages and pip libraries
Excellent VS Code integration
Limitations
Requires coding knowledge (not suitable for non-developers)
Less visual than n8n or Make
Smaller integration library than visual-first tools
Learning curve if you’re used to pure no-code
Pricing: Free plan with 10,000 invocations/month. Paid plans start at $29/month.
vs n8n: More developer-friendly with better code environment and serverless architecture. Choose Pipedream if your team codes daily and wants serverless. Choose n8n if you want balance between visual building and code customization with self-hosting control.
Quick Comparison Table
Tool
Best For
Integrations
Self-Hosting
Pricing Model
Learning Curve
AI Capabilities
n8n
Technical teams, high-volume workflows
400-1,200
Yes
Execution-based
Steep
Strong (LangChain, agents, MCP Supported)
Make
Non-technical teams, visual complexity
3,000+
No
Operation-based
Moderate
Limited
Zapier
Speed, mainstream apps
8,000+
No
Task-based
Easy
Moderate
Activepieces
AI-first, truly open-source
594+
Yes
Execution-based
Moderate
Excellent (MCP support)
Gumloop
AI workflows for non-devs
Moderate
No
Varies
Easy
Excellent
Pipedream
Developer workflows
1,000+
No
Invocation-based
Steep
Good
When n8n is Still Right Choice?
Not every search for alternatives should end in switching. n8n still win in specific scenarios.
You have developer resources. If your team includes developers or technical operations staff, n8n’s flexibility pays off. The learning curve becomes irrelevant when someone can write JavaScript in the Code node or read API documentation for HTTP requests.
You need full data control. Healthcare, finance, government, or any regulated industry with data sovereignty requirements. Self-hosting n8n on your infrastructure means data never leaves your control. That’s impossible with cloud-only platforms like Zapier or Make.
You’re running high-volume workflows. A workflow that executes 100,000 times per month costs $50-100 on n8n Cloud. That same workflow might cost $500+ on Zapier or Make. For volume, n8n’s execution-based pricing is unbeatable.
You want to contribute or fork the codebase. Even though n8n uses a Sustainable Use License (not pure open-source), you can fork it, modify it, and contribute back. That’s more flexibility than any closed-source alternative.
You’re building complex, unique workflows. When your automation needs don’t fit templates or pre-built integrations, n8n’s Code node and HTTP Request node let you build anything. I’ve built custom error handling, rate limiting, and data transformation logic that would be impossible in Zapier’s linear model.
Frequently Asked Questions
Is there a completely free n8n alternative?
Activepieces and Pipedream both offer generous free tiers. Activepieces is truly open-source (Apache 2.0) and can be self-hosted with no limits. Pipedream’s free tier includes 10,000 invocations per month. Zapier and Make have free plans but with tight restrictions (100-1,000 tasks/month).
Which n8n alternative is easiest for non-technical users?
Zapier wins for pure simplicity. Linear workflows, massive integration library, extensive templates. Make is second – more powerful than Zapier but still visual and approachable. Gumloop is easiest specifically for AI workflows.
Can I migrate my n8n workflows to another platform?
Not directly. No tool offers import from n8n’s workflow JSON. You’ll need to rebuild workflows manually. The concepts translate (triggers, actions, data flow), but the implementation differs in each platform. Budget 2-4 hours per workflow for rebuilding and testing.
Which alternative has the most integrations?
Zapier with 8,000+, followed by Make with 3,000+. But count isn’t everything. Check if your specific apps are supported and how deeply integrated they are. A platform with 1,000 integrations but deep support for your tools beats one with 5,000 shallow integrations.
What’s the cheapest n8n alternative for high-volume workflows?
Depends on workflow structure. For complex workflows (10+ nodes per execution), n8n’s execution-based pricing is usually cheapest. For simple workflows (2-3 steps) running frequently, Make’s operation pricing might cost less. Self-hosted Activepieces is free but requires infrastructure management. Run your numbers with each platform’s pricing calculator.
Is Make better than n8n?
“Better” depends on your team’s skills and needs. Make is better for non-technical teams building visual automations quickly. n8n is better for technical teams needing customization, high-volume workflows, or self-hosting. Make has more integrations; n8n has more control. Choose based on your situation, not popularity.
Your Next Move
There’s no single “best” automation platform. The right choice depends on your team’s technical skills, workflow complexity, volume, and budget.
If you’re still using n8n and it’s working, don’t switch just because alternatives exist. But if you’re fighting the learning curve, overpaying for executions, or need better non-technical user support, one of these alternatives probably fits better.
Start with free tiers. Build the same test workflow in 2-3 platforms. See which one feels right for your team. The right tool is the one your team will actually use.
And if you decide n8n is still your platform? Check out the rest of The Owl Logic’s n8n tutorials to get more out of it.
I was literally spending 5-8 hours every week manually transferring the data – traffic metrics, source channels, sales figures and more.
Everything was manual, day-to-day grunt work.
As a business owner, when work piles up (and it always does), I’d skip the data processing entirely. Those tasks would just pile up into my backlog, creating even more stress down the line.
Then I thought: enough, Let’s automate with n8n.
I integrated Google Sheets with n8n, and now everything runs on autopilot. EVERYTHING.
In this post, I’m going to show you exactly how to integrate Google Sheets to n8n. Step by step. This is a beginner friendly guide that assumes zero prior automation experience.
Prerequisites
Before we integrating Google Sheets with n8n, make sure following ready.
1. An n8n account or Self-hosted instance.
You’ll need access to n8n to build your workflow. You’ve got two options
n8n cloud (Recommended for beginners): Sign up for free account at n8n.cloud. No installation required, and you start building workflows immediately.
Once your Google OAuth credentials are connected to n8n, come back here continue with the integration workflow.
4. A Google Sheet to Work With
Create a simple Google Sheet to to test the integration. You can use existing spreadsheet or create a new one with simple data.
5. Basic Understanding of Spreadsheets
You don’t need a coding experience here. If you know how to use Google Spreadsheet (rows, columns, basic data entry), you’re ready to go.
Step 1: Setting Up Your First Google Sheet Workflow
Create a New Workflow
Log in to your n8n account
Click on “New Workflows” in the left sidebar
You’ll see a blank canvas – this is where your automation playground
Add a Manual Trigger
Every n8n workflow needs a trigger to start it. For testing purposes, We’ll use a manual trigger, which lets you run the workflow whenever you click the execute button.
Why manual trigger? We want to test everything manually before setting up automated triggers like, schedules, or webhooks. This gives full control during the learning phase.
Rename Your Workflow
Rename the workflow to something useful, Always use clear, descriptive names. When you’re managing 10+ workflows, you’ll thank yourself for this habit.
Step 2: Connect Your Google Sheets Node
Configuring Google Sheets Node
Click on the “+” icon next to your manual trigger.
In the search bar, “Google Sheets”
Click on the “Google Sheets” to add it to your editor.
You’ll see the node configuration panel open on the right side.
There are many operations for sheets, Append, Update, Delete, Create, Get and etc.
For this tutorial, we are going to use GET operation Get row(s) to read what is inside on the Google Sheets.
Credentials to Connect With
Click on the “Credentials to connect with” dropdown and select your Google OAuth credentials that you’d already setup.
From List: Choose from your recent Google Sheets (easiest)
By URL: Paste the full Google Sheets URL
By ID: Use the spreadsheet ID from the URL
For beginners, select “From List” and choose your test spreadsheet from the dropdown.
Sheet: Select Your Sheet Tab
If your spreadsheet has multiple tabs (Sheet 1, Sheet 2, etc). Select which one you want to read from.
In my case, I’m using Sheet 1.(the default tab name)
Step 3: Execute The Workflow
Time to see the magic happen! let’s run this workflow and fetch Google Sheets data.
Run Your First Test
Make sure both nodes are connected (you should see a line in between them)
Click “Execute Workflow”
What happens next?
n8n will connect your Google Sheet, fetch all the rows, and display the data in the output panel below the Google Sheets node.
Congratulations You’ve just fetched (read) data from Google Sheets to n8n.
What you can do from here?
Now that you have sheet data flowing into n8n, the automation possibilities are endless.
Filter and process the data using n8n’s built-in nodes
Send data to other apps like Gmail, Discord, Slack, etc
Transform the data with code, AI or data manipulation nodes
Trigger action based on specific values in your sheet
Append new data back to same or different sheet
Common Google Sheets Operations Explained
Now that you’ve successfully read data from Google Sheets, let’s explore the other essential operations you’ll use most frequently. Understand these will give you the foundation to build any Google Sheets automation with n8n.
Append Row – Adding New Data
When you want to add new entries to the bottom of your spreadsheet (like logging form submission, new leads or daily reports).
How it works?
Add a Google Sheets node to your workflow
Select “Append Row” from operations dropdown
Choose your spreadsheet (document) and sheet
Map the data you want to add to each column
Update Row – Modifying Existing Data
When you need to change information in specific rows (like updating order status, making tasks complete, or changing contact details).
How it works
Add a Google Sheet node
Select “Update Row” from Operations
Choose your spreadsheet (document) and sheet.
Specific which row to update (or use column matching to find the right row)
Map the new values for each column
Important: You need a way to identify which row to update. You can either use
The row number (if you know it)
Match by unique column value (like email or order ID)
Append or Update Row – Smart Data Handling
When you’re not sure if a record already exists. This operation check first, then either updates the existing row or creates a new one.
How it works
Same structure as choosing sheet nodes.
Choose your spreadsheet (document) and sheet
Define the column to check for existing records (like email or ID)
Map your data
Why this is so powerful? This prevents duplicate entries while keeping your data up-to date automatically.
What You’ve Learned
Let’s recap what you’ve learnt in this tutorial
Set up Google Sheets integration with n8n – You connected your Google account and configured OAuth credentials to enable secure communication between n8n and Google Sheets.
Created your first automation workflow – You build a working n8n workflow from scratch, understanding how nodes connect and execute
Successfully fetched data from Google Sheets – You use the “Get Row(s)” operation to pull spreadsheet data into n8n, which is the foundation for any Google Sheets automation
Understood the essential operations – You now know the difference between, Append, Update and Append & Update. When to use each one.
Your Turn & Share Your Progress
The best way to solidify what you’ve learned is to build something yourself.
Got your workflow working? Awesome, here’s what you have to do next?
Experiment freely – try combining different operations, break things, and rebuilt them.
Start small, then scale – don’t jump straight into complex 20-node workflows. Master simple 2 – 4 node flows first.
Document your workflows – Use n8n’s notes feature to add comments explaining what each node does.
Build something real – The best practice project is one that solves an actual problem you have. What manual tasks could you automate today?
Sub-workflows let you call one workflow from another workflow. Instead of rebuilding same automation in multiple places, you build it once and reuse it like a component.
Why it really matters?
Let’s say you’re validating customer addresses (for an example). You check if the street address exists, verify the postal code format, and make sure the city matches the state. That’s maybe 6-8 nodes doing the validation.
Now you need the same validation in three different workflows – one for new orders, one for customer profile updates, and one for shipping.
Without sub-workflows, you copy those 6-7 nodes into all three workflows.
Order workflow: has address validation nodes
Profile workflow: has the same address validation nodes (copied)
Shipping workflow: has the same address validation nodes (copied)
When the postal service changes their validation rules, you have to update all three workflows.
With sub-workflows, you build one address validation workflow. Then your other three main workflows just call it. Update the validation once, and all three workflows automatically use the new rules.
But sub-workflows aren’t always the answer. Sometimes they add complexity you don’t even require. perhaps a simple IF node or loop does the job better.
This guide shows you when sub-workflows actually help.
This is an advanced topic in n8n – If you’re a beginner, better to read our other fundamentals first to understand this topic.
Use sub-workflows when you’re dealing with one of these situations
Reusable logic across workflows – When you’re copying the same node group into multiple workflows. Data validations, API calls with specific error handling, report generation, anything you do more than once is a candidate.
Memory issues in a massive workflows – Workflows with 50+ nodes can hit memory limits. Breaking them into smaller sub-workflows gives each piece it’s own execution context
Modular testing – Testing a 50-node workflow means running all 50 nodes. Testing a 10-node sub-workflow in isolation is faster and catches issues earlier.
Human-in-loop – Need approval, sub-workflows handle these cleanly and return the approval result to the parent.
Don’t use sub-workflows when
Simple branching is enough – An IF node handles “do X if true, Y false” that’s totally fine. Don’t build a sub-workflow for a 3-node branch
When you’re doing batch processing – Loop over items processes arrays within a single workflow.
If you’re never going to reuse it, and then don’t use it.
How Sub-Workflows Work
A sub-workflow is just a regular n8n workflow that another workflow calls.
The parent workflow sends data to the child workflow. The child does it work. The it sends results back to the parent.
Here’s what happens
Parent workflow hits a Execute Sub-workflow node
Data get passed to the child workflow
Child workflow runs from start to finish
Final output returns to the parent
Parent continues with the returned data
By default, the parent wait for the child workflow to complete. The workflow execution pauses at Execute Sub-workflow node until the child finishes.
I checked that Sub-workflow executions don’t count toward your monthly execution quota if you were in n8n cloud. n8n only counts the parent workflow execution. This means you can call sub-workflows as much as you want without burning through your plan limits.
We’ve lists of customer data, We need to verify their address and update the Verification Status as Order Approved
Let’s create the workflow
Add a Manual trigger
Read the sheet – Spreadsheet with customer detail
Execute the sub-workflow – We validate the address in a different workflow
Update the Verification Status as Order Approved (This is the final output)
1. Add a Manual Trigger + Google Sheet (Read)
2. Add Execute Workflow Node
Go to Execute Workflow node, and select the Workflow, either Create a new Sub-Workflow or use the existing one. I create a new.
Once you selected the created a new, then n8n will opening up a new tab with that workflow.
In the new created sub-workflow, you have to add the Input data mode, for this tutorial, I go with Accept all data which means fetch all incoming data from the parent workflow.
Now you’ll see like this in your sub-workflow. and make sure to Save and Publish your sub-workflow otherwise it will throw errors.
3. Adding the Core Logic
We need to validate only for new customers,
in our customer sheet, there is a column called “Return Customer”
which means we can validate it with. If return customer is “Yes” then we don’t need to validate his/her address.
If it’s “no” then we need to just validate it.
4. Connecting Final Sheet Update Node
Now, we are going to connect the final the sheet node to update the verification status to Order Approved
Go to Update row in a sheet – make sure to match the columns, and change the verification status to Order Approved.
5. Execute the Parent Workflow
Well, the sub-workflow is executing on its own and the parent workflow on-halt because child-workflow must pass down the data to the parent.
What happened in the sub-workflow? aye, let’s see it.
Go to your sub-workflow, and executions to see all the data output.
this is the final output, and yeah, This is not a big tutorial workflow, yet this gives you a glimpse on when to use sub-workflow.
Passing Data Between Workflows
The trickiest part of sub-workflows is getting data in and out correctly.
Sending Data to the Sub-Workflow
When the parent workflow calls a sub-workflow, it passes data through the execute Sub-workflow node.
In our tutorial example, the parent workflow reads 17 customer records from Google Sheets. When it hits the Execute Sub-Workflow node, those 17 records get sent to the child workflow.
The child workflow receives this data in it’s trigger node – the Execute Sub-workflow Trigger (also called as “When Executed by Another Workflow)
// Parent sends these 17 customer records:
[
{
"Customer Name": "Christopher Lee",
"Product": "Graphics Tablet",
"Return Customer": "No",
"Address": "8421 Pine Road"
},
{
"Customer Name": "David Wilson",
"Product": "Laptop Pro 15",
"Return Customer": "Yes",
"Address": "3156 Sunset Lane"
}
// ... 15 more records
]
// Child workflow receives all 17 records in the trigger node
If you set the Execute sub-workflow mode to “Run once with all items“, the sub-workflow gets all 17 records in one executions.
If you set it to “Run once for each item“, the sub-workflow runs 17 times – once per customer record.
Receiving Data From The Sub-Workflow
Here’s where people get confused, (yes, I did too, so please you don’t)
The Sub-Workflow’s last node output becomes the parent workflow’s Execute Sub-Workflow node output.
Not the input. Not some middle node. The final node.
In our tutorial, the sub-workflow ends with two Google Sheet updates nodes (one for return customers, one for new customers). The combined output from both update operations return to the parent workflow.
Sub-workflow updates appropriate columns for each group
Sub-workflow returns all 17 updated records to parent
Parent workflow receives 17 records with updated verification data
Parent workflow updates final “Verification status” column to “Order Approved”
Frequently Asked Questions (Real)
I’ve an existing nodes in placed primary workflow? How do I copy instantly?
Yes, you can do this, make sure to highlight the nodes you want, and right click, select the option as create sub-workflow. This way faster than manual setup when refactoring existing workflows.
Or press alt + x
What You Just Learned
Sub-workflows solving specific-problems, reusing logics, breaking up massive workflows or isolating testable components
They’re not always the answer. Sometimes an IF node does the job better. Sometimes keeping everything in one workflow is simpler.
But when you find yourself copying node groups between workflows, or when your workflow canvas looks like a tangled mess, that’s when sub-workflows shine.
Start with something simple. Extract your most-copied logic into a sub-workflow. See how it feels. The expand it from there.
Here’s something no one tells you when you’re learning n8n. 80% of the time, you don’t need Loop Over Items (split it batches) node.
I started my n8n workflow by building it connecting dots, so I added this node (loop over items) to the editor, and I was passing data from point A to B to C iteratively, and sending one by one without getting interrupted, then I removed the “Loop over items”, surprisingly it worked but faster.
Certain nodes will only process the first item and ignore the rest.
This guide will show you exactly when Loop over items is necessary with real use case scenarios, when it is completely unnecessary to add, and how to avoid the mistakes that broke my workflows when I was learning.
This is first time I ever posted a workflow on reddit, and I received 162k views and 400+ upvotes. I think this is 10 months ago, and I used first time ever Loop over items.
How n8n Actually Processes Multiple Items?
Most n8n nodes process ALL items automatically. That’s built into how n8n works.
Say you pull 100 rows from Google Sheets. Each row becomes an “item” in n8n. When that data hits your next node, maybe a slack node that post messages. n8n automatically runs that Slack node 100 times. One message per item.
You don’t build a loop. You don’t configure anything special. It just happens.
This is different from Make.com(formerly Integromat). In Make, use an iterator to process items one by one through entire workflow.
In n8n, each node processes all items, then passes results to next node.
Most beginners (past me) assume they need Loop over items because that’s how worked in Make. They actually don’t. n8n already handles it.
Run it, look at the output. You’ll see 5 items, all processed, no loop needed.
The 3 Scenarios Where You Need Loop
Scenario #1: Rate Limiting & API Throttling
APIs limit how many requests you can make per minute. Gmail allows 100 emails per minute. Most APIs cap at 60-120 requests per minutes. Some are trickier.
If you’ve 500 items and n8n’s default behavior processes them all at once, you’ll hit that limit. Your workflow fails. The APIs returns error, sometimes it process, other’s don’t.
Solution
Option 1: You need to add wait (1 minute) to the end of node.
Option 2: Batch your items 1 or 10. (totally depends on your requirement)
If you’re calling external APIs for many items, you probably need this. Everything else in n8n handles multiple items automatically, but external APIs don’t care about n8n architecture – They’ll block you for too many requests.
Scenario #2: Node Exceptions (Nodes that Don’t Auto Loop)
Some nodes only process the first item by default design. It’s not a bug, and it’s how those specific nodes work.
The most common example: RSS Feed Read Node
You give it an array of 5 RSS URLs. Most nodes would fetch all 5 feeds. RSS feed read node? Only fetches the first one and ignore the rest.
Why? The RSS Feed read node expects a single URL, fetches the feed, and returns multiple items (contents). It’s designed for “One feed in, many contents out” – not “many feeds in”
If you need to read multiple RSS feeds, then you need to connect with Loop over items.
n8n’s official doc maintains a list of node exceptions. Before assuming thing up, check if you node is on that list.
Other nodes that might need loops
Certain webhooks nodes that expects a single request
Some database nodes with specific query patterns
Custom nodes that process items differently
Scenario #3: Pagination with Unknown Page Counts
APIs that return paginated data sometimes don’t tell how many pages are exist.
They just say here “page 1”, “page 2” without revealing total pages.
well, let’s move on with a workflow here. This is going to be a little bit advanced workflow, and though you would need to understand these before you move to this workflow, otherwise you can still try it out with me and see whether you understood or else check it out the references here.
So what we are doing here is, we are going to send a request to n8n subreddit to fetch posts, and we are limiting to 25 posts, that’s why we are creating a limit = 25
This is how it should look like. Please be make sure to compare your workflow with the image.
Code review: When the workflow starts, you begin with an empty bag (allPosts: []). you also have instructions on which sub-reddit to visit (subreddit: "n8n") and a note that says “start from the beginning” (after: null)
HTTP request node goes to reddit, and says “give me 25 posts from r/n8n starting from the beginning “.
Reddit responds with 25 posts AND a special ticket that says “if you want more posts, come back with this ticket: t3_abc123”
Now your Code node runs. It looks at three things.
First: It grabs those 25 new posts.
Second: It grabs that special ticket for getting more posts later
Third: it checks your shopping bag to see what you already collected from previous trips. on the first tip, array is empty, on the second trip, your array has 25 posts, on the third trip, it has 50 posts.
Now we can fetch posts from reddit based on pagination by limiting.
Quick Troubleshooting
Loop processes the same item every time?
You’re using .first() inside the loop. This literally means “always grab the first item” – not the current one.
// ❌ Wrong - always returns first item
{{ $('Google Sheets').first().json.email }}
// ✅ Right - returns current item
{{ $json.email }}
Loop never stops?
You’re missing an exit condition. Every loop needs a way out. Like, we need to safety limit the fetches data. Make sure to test with small datasets first.
Quick Decision Flowchart
Use this every time you think you need Loop Over items
Start – Do you have multiple items to process?
No – You don’t need any loop
Yes – Next,
Does the next node automatically process all items?
Yes – No Loop Over Items needed
No – Next,
Check these 3 reasos
Need rate limiting – Loop Over items with batch size + Wait node
Node exception (RSS, etc) – Loop Over items with batch size 1
Pagination scenario – Loop Over Items with reset option on.
None of the above – reconsider workflow
Still not sure, Test without Loop over items first. If items 2-100 aren’t processing, then add it.
When to Skip Loop Over Items Entirely
You do NOT need Loop over items for,
Standard data processing: Google Sheets, databases, Airtable – they all return multiple items. n8n processes them automatically.
AI/LLM operations: OpenAI node, Claude node – they handle batching internally.
Most HTTP requests: Unless the API specifically has rate limits you’re hitting, n8n will make all requests.
Basic transformations: Code nodes, Set nodes, Filter nodes – all process multiple items by default.
Email to small groups: Sending 50 emails? n8n handles it. Sending 500? Now you might need rate limiting.
The rule: If you can’t explain in one sentence WHY you need Loop Over Items (using one of the three scenarios), you don’t need it.
Final Thoughts
Loop Over Items is powerful when you need it. But I’ve reviewed dozens of community workflows, and 80% of them have unnecessary Loop Over Items nodes, and some add another loop inside the main loop. I’ve zero idea, what they are trying to accomplish.
The pattern I follow is build without it first. Only add when I hit one of three situations – rate limit, node exceptions, or pagination
You workflow will be simpler. It will run faster. And when you DO need Loop over items, you’ll know exactly why.
Workflows becomes useful when they make decisions. You need to route customer emails differently based on domain, process high-value orders separately, or send urgent tickets to different teams. That’s conditional logic – having your workflow ask questions and take different action based on the answers.
I’ll show you how to build decision-making into your n8n workflows using IF nodes, switch nodes and conditional expressions. more importantly, I’ll show you how to troubleshoot when things don’t work as expected, because that’s where most people get stuck.
The IF node looks at your data and splits it into two paths: true and false. Data that matches your condition goes down the true path. Everything else goes down the false path.
Here’s what make this powerful: both branches can have completely different workflows attached. Your true branch might send a Slack message while your false branch updates a database. The IF node routes each piece of dat to where it needs to go.
The data itself doesn’t change when it flows through an IF node. If you send in a customer record, that exact same customer record comes out, just on either true or false output.
IF node inspects the data and makes a routing decision, but the data stays intact.
Understanding how data flows through conditional nodes is critical. I cover the fundamentals in my guide on how n8n workflow nodes handle data, but here’s the quick version, when an IF node receives multiple items, it evaluates each one individually. Some items might go true, others false. Both outputs can have data at the same time.
Key Takeaways
Learn how to filter and route data using IF and Switch nodes with real customer scenario (Example of real use case provided)
Find out why your condition fails (data type, mismatches, case sensitivity) and how to fix them
mixed AND/OR logic using expressions when the UI dropdown isn’t enough
Download the workflow JSON below to import and test it in your n8n instance, or follow the step by step guide
How to Setup Your First IF Node
Let me walk through a practical example, filtering customer orders, Let’s assume, we are going to give a discount for those who have purchased above $1000 for upcoming holiday season, and we are going to send them emails. Well, we have a lists of customer db with purchasing value. Let’s break down the core objectives.
Our goal is,
Find customers who had purchased over $1000
Get their email address.
Start email sequence for Holiday season reminder with VIP 20% Offer above $1200 purchases.
Don’t do anything to those who purchased below $1000
What do you think it will happen now? you got any guesses here.
Yes, you get 9 customers who had purchased over $1000, and then you can add any node, like gmail, to send them a message with coupon code, or whatsoever.
Same for false branch too, either you can send to sheets or whatsoever, that’s totally up to you how you want to manipulate the data you have.
Understanding Data Types and Comparison Operators
Every comparison in an IF node requires you to select a data type. This tells n8n how to interpret the values.
Comparing “1000” as a string different from comparing 1000 as a number.
Let’s expand our customer workflow (what we did earlier with IF) We filtered by purchase amount, but what if we only target US customers? or customers who haven’t received the emails recently?
Each of these checks different data-type.
Choosing the Right Data Type
String: Text data like names, emails, or country codes. Use this when comparing text, even if the text contain numbers (like phone number or zip codes)
Number: Numeric values for mathematical comparisons. Purchase amounts, quantity, age, or any value you need to compare them mathematically.
Boolean: True or False values. Use this when checking if something else enabled, or active. for an example: John doe is a US customer, so in the sheet there is a column header with US country, true.
Date & Time: Timestamps and dates. Use this when comparing if something happened before or after specific times.
// Number - what we already did
{{ $json.purchaseAmount }} larger than 1000
// String - check if customer is in US
{{ $json.country }} equals "US"
// String - check email domain
{{ $json.email }} contains "@gmail.com"
// Boolean - check VIP status
{{ $json.isVIPMember }} equals true
// Date & Time - check recent activity
{{ $json.lastPurchaseDate }} after "2026-01-23"
Common Operators Mistakes (And How to Avoid Them?)
Using “equals” when you need “contains” – You want to check if customer uses Gmail.
Don’t check if email equals “gmail.com”. The full gmail is “customerx@gmail.com”, which doesn’t equal “gmail.com”. Use contains instead.
// Wrong
{{ $json.email }} equals "gmail.com" // Never matches
// Right
{{ $json.email }} contains "@gmail.com" // Matches customer@gmail.com
Comparing numbers stored as strings: your database might return purchase amount as strings, If your data "1000" (with quotes), that’s a string.
Comparing it as a number might not work because n8n tries to convert, but it is unpredictable.
Check your actual data type in the expression editor. If you see quotes around the number, convert it first.
{{ Number($json.purchaseAmount) }} larger than 1000
or just use string comparison if that makes more sense for your data.
{{ $json.purchaseAmount }} equals "1000"
Date format mismatches – Dates come in many formats. n8n expects ISO 8601 Format (2026-01-15T10:30:00Z) for reliable comparisons. If your dates look like “01/15/2026” or “January 15, 2026”, you’ll need to convert them first.
Case sensitivity: String comparisons are case-sensitive by default. “US” doesn’t equal “us”. If you need case-insensitive matching, convert both values to lowercase first:
{{ $json.country.toLowerCase() }} equals "us"
Building Workflow #2 With New Data
I hope, I didn’t bore you guys, you need to learn and understand this to keep your head clean when you manipulating or mapping data. let’s go.
Now our sheet has US Country on F Column , and I added boolean value TRUE / FALSE.
Our objective is to send emails to those who purchased over $1000 US customers.
Now we need to map the US country to value.
Here’s the steps you need to take.
Drag the US country parameters, directly to the value, then n8n automatically detects it’s data type.
After that you need to select whether it is true or false.
I select “TRUE” because that’s my core objective.
You can execute the workflow now.
Yay, we got it. but n8n do all the heavy lifting for your when you drag and drop the parameters, still, you need to understand what is happening behind the scenes.
Data Type Reference
Data Type
Common Operators
When to Use
String
equals, contains, starts with, ends with
Email domains, country codes, status values
Number
larger, smaller, equals, between
Purchase amounts, ages, quantities
Boolean
equals (true/false)
Active status, feature flags, yes/no fields
Date & Time
after, before, between
Last purchase date, signup date, deadlines
Building Multiple Conditions (AND/OR Logic)
In the previous section, we added a second condition to our IF node using the AND operator. Wait, where did I add that?
Yes, It is basically chaining if the conditions are equals then it will provide us filtered data. so we checking greater than or equals to 1000, and customer need to be in United states, if both conditions are satisfies, then n8n passed the appropriate data to next node.
What AND Logic Does?
Customer A: $1200 purchases, US customer – Both true – Goes to true output
Customer B: $1200 purchase, UK customer – One false- goes to false output
Customer C: $800 purchase, US customer – One false – goes to false output
Customer D: $800 purchase, UK customer – Both false – goes to false output
Only Customer A gets the email, AND logic is strict, everything should match.
Understanding OR Logic
Now let’s change the business rule. Marketing team wants include VIP members even if they are spent less than $1000.
Let’s practically work on this in a workflow I guess, that would seems easier to understand.
Workflow #3 AND OR Logic Combined
so as we said, we need to add a new rule here. We are going to add a new column as VIP.
Here’s where the IF Node UI gets tricky. You can’t mix AND or OR operators in the dropdown. When you change the dropdown, n8n changes ALL the operators.
If you have three conditions connected by AND and change one dropdown to OR, all three becomes OR.
Alright, so how do we gonna solve this?
We use expressions. Instead of multiple conditions with dropdown, we write the entire logic as a single expression
Our new business rule is: Send email if customer has (amount >=1000 AND is a US customer) OR VIP customer.
so we are adding an expression stating in the codeblock, and we are expecting a boolean value, which means it should true or false, In our case we need to our values to be true. so we are setting up the values which is boolean is true.
let’s execute and see.
We get 11 customers who match our logic, some because they are high-value US customers, some because they’re VIP customers, and some because they meet both criteria.
Switch Node: Route Data to Multiple Paths
IF node splits your workflow to two directions.
Switch splits into as many direction as you need.
When to use Switch nodes?
So, basically I wanted to give you with the examples of the current workflow, that we are sending customers emails who purchases over $1000 and US based.
Sort customers by spending tiers (Gold, Bronze, Silver)
Route support tickets by priority (Low, Medium, High, Urgent)
Send different emails based on locations (US, UK, Asia, EU)
Handle different product categories with unique logic.
alright, now you maybe asking, “yeah, we can use it with IF Nodes right?”
Yes, but you should know that when to use IF Nodes?
Simple yes/no answers.
Just two paths needed
Binary choices (approved, rejected, or active/inactive)
Switch has two modes,
Rules mode: Visual condition (what we’ll use)
Expression mode: Write code to calculate output number
We’ll use visual mode since it’s more practical for our users.
Workflow #4 Routing by Customer Tier
Let’s route our customers to different email campaigns based on spending levels, In marketing, we should not always treat always who spend higher, even though who spend lower. Engagement is the key.
We have three customer tiers
Bronze: $0 – $500
Silver: $501 – $1000
Gold: $1001+
Connect the switch node to the sheets.
You’ll get this interface, sometimes you won’t see the parameters on the left side node, which means you have to execute them individually to to see the output data of the previous node. Check the reference image below for your understanding
Click on that “Play” icon to execute that node, but not the workflow. Got it. let’s move to the next phase.
You have to configure this as it is for your criteria or business needs, for now I wanted to create the bronze tier, and it’s done.
Let’s create other tiers as well – click on the Add routing rule to create another route for “Silver tier”
but silver tier is tricky, I’ll let you to solve it, if you couldn’t find the answer then scroll down.
In this scenario, we have to check two comparisons for silver tier
It should greater than 500
and less than 1000
But, you cannot write two comparisons in a single field, that’s where we have to use expressions here.
Finally I was able to get the customers list based on tier types, this is how you have to use Switch nodes appropriately. By blending expressions and with help of UI as well like dragging and mapping.
I hope you understood this module clearly, but I prefer to read multiple times, and try with different scenario. This way you can build a great things in the future easily.
I do believe in one thing certainly that mastering basics can do anything.
Q. Why isn’t my IF node working even though my condition looks correct?
The most common issue is a data type mismatch. If you’re value stored as as text “1000” but you comparing it as numbers, the condition will fail. Check your data type in the IF node dropdown, or toggle on the convert types where required option
Q. Can I use OR logic IF Node dropdown?
Yes, if it’s for a single comparison, but you cannot mix with OR + AND operators, to do this multiple comparisons, then you have to use chain comparisons with expressions. e.g `|| &&”
Q. My Switch node is sending customers to the wrong output
Switch sends data to the FIRST matching rule by default. If you have any overlapping conditions (like checking silver tier before the gold tier), customers might match the wrong rule first. Always order your switch rules to specific to least specific for best case scenario.
Final thoughts
You’ve already learned how conditional logic works in n8n. Now it’s time to build your own workflows. Start simple, and correlate with your ideas. Send us your Switch related workflows to us to review.
Send it to: hello@theowllogic.com
Now go build something useful, for additional references, you can check below the resources.
Building workflows in n8n without expressions similar to driving with first gear, well you can move forward, but you’re missing out the other gears that you can travel faster. It’s like totally missing out 90% of the utility power.
Similar to the above analogy, you’re totally missing out if you’re not using expressions.
I learned this in a hardway when I built my first “real-real” workflow.
Grabbed data from a webhook, tried to send it to Google Sheets. Workflow ran perfectly in testing. Once deployed it immediately threw error.
The problem? The webhook sometimes sent firstName and sometimes first_Name. Sometimes the email field was nested under body.contact.email, other times just email. My rigid workflow couldn’t handle the variations.
That’s when I discovered aha there is expressions. Not just {{ }} syntax everyone mentions, but the actual pattern for handling messy, real-world data. The difference between workflows that run once during testing versus workflows that survive actual production use.
This guide shows you the expressions I wish I’d learned on day one or two. Not theoretical syntax, but copy-paste patterns you’ll actually use when webhooks send inconsistent data, APIs return nested objects, and you need to transform 500 items without crashing.
What are n8n Expressions? (And, Why They Are Important)
Here’s the simplest way to think about expressions.
They turn static workflow parameters into dynamic ones that adapt to your actual data.
Without expressions, every value in your workflow is hardcoded.
Want to send an email? You’d type “john@example.com” directly into recipient field. That works exactly once, for exactly one person.
With expressions, you tell n8n “grab the email from the data that just came in” Same workflow now handles John, Sarah, and 10,000 other people without changing a single thing. Well, you got me there, but you can ask? Shajid, we can directly map the key, then whatever the John, Sarah, Michael could come in automatically? That’s correct. but We are going beyond just mapping by drag and drop values. Keep reading.
Under the hood, n8n uses Tournament, a templating language it developed specifically for workflow automation.
Tournament provides the {{ }} syntax and handles JavaScript execution. n8n extends Tournament with custom variables ($json, $node, $input), transformation functions, and built-in libraries like Luxon for dates and JMESPath for JSON querying.
The syntax is just double curly braces wrapping JavaScript code
{{ $json.email }}
That’s it. Everything inside {{ }} executes as JavaScript and gets replaced with the result. Static becomes dynamic.
You’ll use expressions constantly because real workflows process dynamic data. Form submission from different people. API responses that change. Customer names, order amounts, timestamps, Any time data flows through your workflow, you need expressions to access it, transform it, and pass it along like baton.
If you’re completely new to n8n, check out Your First Hello-World n8n Workflow to get your bearings first. Once you can build a basic workflow, expression are your next step.
Expression Syntax Fundamentals (The {{ }} The Wrapper)
The rule is simple.
If you want n8n to evaluate code instead of treating it as plain text, wrap it in {{ }}.
Click any parameter field in n8n and you’ll see a toggle between Fixes and Expression modes.
Fixed mode is for static values.
Expression mode is where the magic happens.
Inside those curly braces, you’re writing JavaScript. n8n executes it and replaces the expression with whatever it returns.
Common beginner mistake is forgetting the wrapper entirely.
If you see an “expression not recognized” error, you either forgot the {{ }} or you’re still in Fixed mode. Toggle to Expression mode first, then make sure your code is wrapped properly
The expression editor (click the little expansion icon) gives you syntax highlighting and autocomplete. Use it when you’re writing anything longer than a simple field access. Inline mode works fine for {{ $json.email }} but for complex transformation, the editor saves you from syntax errors, and confusion.
Accessing Data: Your First Expression ($json Explained)
Every node in n8n receives data from the previous node. That data lives in $json. Think of it as “the current item I’m working with”
When a webhook fires, form data comes in. That data is now $json. When an HTTP request returns a response, that response is $json. When you read a row from Google Sheets, that row is $json.
Here’s where beginners hit their first real problem. Webhooks are the most common way to start n8n workflows, and webhook data always comes nested under body. You’ll see this structure constantly.
The mistake? Trying {{ $json.email }}. That field doesn’t exist at the root level. You need {{ $json.body.email }}.
When your webhook expressions return undefined, check the actual data structure first. Click the node, look at the output tab, see where your fields actually live. Then write your expression to match that structure.
These are one-line operations. Clean, readable, fast.
The Code node wins when your logic get complex
// This needs the Code node:
const activeItems = $input.all().filter(item => item.json.active);
const total = activeItems.reduce((sum, item) => sum + item.json.price, 0);
const average = total / activeItems.length;
return {
json: {
count: activeItems.length,
total: total,
average: average
}
};
You need variables to store intermediate results. You’re looping through items. You’re building a new object from scratch. These are Code node tasks.
Performance-wise, expressions and Code nodes run at similar speed for small operations. The real difference is maintainability.
Expressions keep your workflow visual and easy to scan. Code nodes hide the logic inside a black box.
Use expressions when you can, Code when you must.
One more thing: Expressions execute per item. If you’re processing 100 items, an expression runs 100 times. The Code node can process all 100 items at once using $input.all(). For complex multi-item logic, Code node is often cleaner.
Beyond $json, n8n gives you several built-in variables for different scenarios.
$input – Working with Multiple Items
Most expressions use $json, which represents the current single item. But workflows often process multiple items at once. That’s where $input comes in.
// Get all items from previous node
{{ $input.all() }}
// Get first item only
{{ $input.first().json.email }}
// Get last item
{{ $input.last().json.name }}
// Current item (usually same as $json)
{{ $input.item.json.id }}
When would you use this? Your workflow fetches 50 customer records from a database. You want to send one email that lists all 50 names. You can’t use $json.name here, that’s only the current item. You need $input.all() to access every item at once.
Another common pattern is you have multiple items but only care about the first one. Maybe you’re looking up a user by email and database returns a list. Use $input.first().json to grab that single record.
$node["Node Name"] Cross Node Data Access
Sometimes you need data from a node that’s not directly before the current one. Maybe you fetched user details in earlier HTTP request node, then did some processing, and now you need that original user data again.
// Reference data from a specific earlier node
{{ $node["HTTP Request"].json.userId }}
// Node names with spaces need brackets
{{ $node["Get User Data"].json.email }}
Note: node names are case-sensitive. {{ $node["http request"] }} won’t find a node named “HTTP Request”. Copy the exact name from your workflow.
“Referenced node is unexecuted” error means that node hasn’t run yet. Check your workflow path. If nodes run conditionally (through an IF node, for example), the referenced node might be on a path that didn’t execute.
$now – Timestamps and Dates
n8n includes Luxon for date handling, and $now gives you the current timestamp
// Current Unix timestamp
{{ $now }}
// Format as readable date
{{ $now.toFormat('yyyy-MM-dd') }}
// Calculate future dates
{{ $now.plus({ days: 7 }).toISO() }}
// Calculate past dates
{{ $now.minus({ months: 1 }).toFormat('yyyy-MM-dd') }}
This is useful for timestamping records, scheduling future actions, or filtering data by date ranges.
// Using + operator
{{ $json.firstName + " " + $json.lastName }}
// "John" + " " + "Doe" = "John Doe"
// Building URLs
{{ "https://api.example.com/users/" + $json.userId }}
// Template literals (backticks) for cleaner syntax
{{ `Hello ${$json.name}, your order #${$json.orderId} is ready` }}
Template literals are cleaner when you’re mixing text and variables. The backtick syntax (`) lets you drop variables directly into strings without concatenation. They also support multi-line text:
// Multi-line email template
{{ `Hi ${$json.name},
Your order #${$json.orderId} is ready for pickup.
Total: $${$json.total}
Thanks for your business!` }}
The backtick approach is especially useful when building formatted messages, HTML content, or API payloads where readability matters.
Extracting parts of text
// First 10 characters
{{ $json.description.substring(0, 10) }}
// Everything after position 5
{{ $json.code.substring(5) }}
// Split on space, get first part
{{ $json.fullName.split(" ")[0] }}
// "John Doe" → "John"
{{ $json.email.includes("@gmail.com") }}
// Returns true or false
Use this in IF nodes to route data based on content. Gmail users go one path, everyone else goes another.
n8n Specific String Methods
n8n extends JavaScript with custom transformation methods that make common data cleaning tasks easier. These methods are unique to n8n and aren’t available in standard JavaScript.
Method
Description
Example
extractEmail()
Extract email from text
{{ "Contact: user@example.com".extractEmail() }}
.extractDomain()
Extract domain from URL/email
{{ "https://example.com/path".extractDomain() }}
.toTitleCase()
Convert to Title Case
{{ "hello world".toTitleCase() }}
.toSentenceCase()
Capitalize first letter only
{{ "HELLO WORLD".toSentenceCase() }}
.toSnakeCase()
Convert to snake_case
{{ "Hello World".toSnakeCase() }}
.removeMarkdown()
Strip markdown formatting
{{ "**bold** text".removeMarkdown() }}
.hash()
Generate SHA256 hash
{{ "password123".hash() }}
.isEmpty()
Check if string is empty
{{ $json.field.isEmpty() }}
.isNotEmpty()
Check if string has content
{{ $json.field.isNotEmpty() }}
These methods are particularly useful for cleaning user input from forms:
// Extract email from messy contact field
{{ $json.body.contact.extractEmail() }}
// "Please contact me at john@example.com or call" → "john@example.com"
// Standardize company names
{{ $json.company.toTitleCase() }}
// "ACME CORP" → "Acme Corp"
// Create URL-friendly slugs
{{ $json.title.toSnakeCase() }}
// "My Blog Post Title" → "my_blog_post_title"
String Transformation Quick Reference
Operation
Method
Example
Result
Uppercase
.toUpperCase()
{{ "hello".toUpperCase() }}
HELLO
Lowercase
.toLowerCase()
{{ "HELLO".toLowerCase() }}
hello
Trim spaces
.trim()
{{ " text ".trim() }}
text
Replace text
.replace(old, new)
{{ "hello".replace("h", "j") }}
jello
Replace all
.replaceAll(old, new)
{{ "a b a".replaceAll("a", "x") }}
x b x
Check contains
.includes(text)
{{ "hello".includes("ell") }}
true
Get length
.length
{{ "hello".length }}
5
Substring
.substring(start, end)
{{ "hello".substring(0, 3) }}
hel
Split to array
.split(separator)
{{ "a,b,c".split(",") }}
["a","b","c"]
Array Operations (Working With Lists)
Arrays are everywhere in n8n. API responses return lists of items. Webhooks send arrays of selections. You’ll need these operations constantly.
Getting array length
{{ $json.items.length }}
// Returns number of items in the array
This is perfect for displaying comma-separated lists in emails or notifications.
Filtering arrays
// Get only active items
{{ $json.users.filter(u => u.active) }}
// Get items over a threshold
{{ $json.products.filter(p => p.price > 50) }}
Filter returns a new array containing only items that match your condition. The u => u.active syntax is an arrow function – u represents each item as you loop through.
Map transforms each item in an array. You’re taking a complex object and pulling out just one field from each.
Chaining operations
// Get active users' emails as comma-separated string
{{ $json.users
.filter(u => u.active)
.map(u => u.email)
.join(", ")
}}
This is where arrays get powerful. Filter first to narrow down items, map to extract the field you need, join to create readable output. Three operations, one expression.
Checking if array includes something
{{ $json.tags.includes("urgent") }}
// Returns true if "urgent" is in the array
Here’s a real-world example that combines several of these, and this comes in handy.
// Webhook receives form with checkboxes
{
"body": {
"interests": ["automation", "productivity", "ai"],
"name": "John"
}
}
// Create readable text from interests
{{ "User " + $json.body.name + " is interested in: " + $json.body.interests.join(", ") }}
// Output: "User John is interested in: automation, productivity, ai"
Operation
Method
Example
Result
Get length
.length
{{ [1,2,3].length }}
3
Access item
[index]
{{ ["a","b","c"][1] }}
b
Join to string
.join(separator)
{{ ["a","b"].join(", ") }}
a, b
Filter items
.filter(condition)
{{ [1,2,3].filter(n => n > 1) }}
2
Transform items
.map(transform)
{{ [1,2,3].map(n => n * 2) }}
[2,4,6]
Check includes
.includes(value)
{{ ["a","b"].includes("a") }}
true
Find first match
.find(condition)
{{ [1,2,3].find(n => n > 1) }}
2
Get subset
.slice(start, end)
{{ [1,2,3,4].slice(1, 3) }}
[2,3]
Advanced JSON Querying with JMESPath
When you’re working with deeply nested JSON or need to query complex data structures, dot notation can get messy fast. JMESPath is n8n’s built-in solution for powerful JSON querying without reaching for the Code node.
JMESPath (JSON Matching Expression paths) lets you extract, filter, and transform JSON data using a query language specifically designed for this purpose. n8n includes it by default.
// Extract all order totals from array of orders
{{ $jmespath($json, "orders[*].total") }}
// Input:
{
"orders": [
{"id": 1, "total": 99.99},
{"id": 2, "total": 149.50}
]
}
// Output: [99.99, 149.50]
The [*] syntax means “all items in this array.” Much cleaner than looping with map.
Filtering arrays
// Get only active users
{{ $jmespath($json, "users[?active==`true`]") }}
// Get products over $50
{{ $jmespath($json, "products[?price > `50`]") }}
// Get orders from specific customer
{{ $jmespath($json, "orders[?customerId==`12345`]") }}
The [?condition] syntax filters arrays. Note the backticks around values – JMESPath requires them for literals.
This is powerful for cleaning up API responses before sending them to another service. Extract only what you need and rename fields in one expression.
When to use JMESPath vs dot notation
Use dot notation for simple field access:
{{ $json.user.email }} // Simple, clear
Use JMESPath when you need to,
Query arrays without explicit looping
Filter data based on conditions
Extract and reshape nested structures
Avoid writing complex Code node logic
Real world example – processing Stripe webhook
// Stripe sends nested invoice data
// Extract all line item descriptions where amount > $100
{{ $jmespath($json.body.invoice, "lines.data[?amount > `10000`].description") }}
// Gets descriptions of all line items over $100 (Stripe uses cents)
JMESPath won’t replace all your array operations, but for complex queries on nested data, it’s significantly cleaner than chaining multiple maps and filters.
Working with Objects
Objects are everywhere in n8n – API responses, database records, form data all come in as JavaScript objects. Beyond basic field access, you’ll need these operations for combining data, checking properties, and transforming structures.
Accessing objects properties dynamically
Sometimes you don’t know the exact field name ahead of time. Maybe it’s based on user selection or comes from another node.
// Static access (you know the field name)
{{ $json.user.name }}
// Dynamic access (field name comes from data)
{{ $json.user[$json.selectedField] }}
// If selectedField = "email", this accesses $json.user.email
Iterating over object properties
// Get all property names (keys)
{{ Object.keys($json.settings) }}
// {theme: "dark", lang: "en"} → ["theme", "lang"]
// Get all values
{{ Object.values($json.settings) }}
// {theme: "dark", lang: "en"} → ["dark", "en"]
// Get key-value pairs
{{ Object.entries($json.settings) }}
// {theme: "dark", lang: "en"} → [["theme", "dark"], ["lang", "en"]]
Object.entries() is particularly useful when you need to loop through properties in a Code node or build custom output.
Spread syntax (...) is cleaner than manually copying properties. Later properties override earlier ones if there are duplicate keys.
Checking if properties exist
// Check if property exists
{{ "email" in $json.user }}
// Returns true if user object has email property
// Check using hasOwnProperty (more precise)
{{ $json.user.hasOwnProperty("email") }}
The in operator checks the entire prototype chain. hasOwnProperty() only checks the object itself. For workflow data, they usually give the same result.
Practical example – combining data from multiple nodes:
// Merge user details from database with form submission
{{
{
...($node["Database Query"].json),
...($json.body),
updatedAt: $now.toISO()
}
}}
// Database returns: {userId: "123", status: "active"}
// Form sends: {body: {email: "new@example.com", phone: "555-1234"}}
// Result: {userId: "123", status: "active", email: "new@example.com", phone: "555-1234", updatedAt: "2026-01-18T..."}
This pattern is common when updating records – grab existing data, merge new data, add a timestamp.
Numbers and Math Operations
Number operations are straightforward. Just remember that JavaScript follows standard math precedence (multiplication before addition, etc.).
The toFixed() method is crucial for dealing with money. JavaScript’s floating point math creates weird results like 0.1 + 0.2 = 0.30000000000000004. Use toFixed(2) to clean that up.
Comparisons
{{ $json.stock > 0 }} // true if stock is positive
{{ $json.price >= 100 }} // true if price is 100 or more
{{ $json.quantity === 0 }} // true if quantity is exactly 0
These return true or false, perfect for IF nodes.
Number Operations Quick Reference
Operation
Method
Example
Result
Add
+
{{ 10 + 5 }}
15
Substract
-
{{ 10 - 5 }}
5
Multiply
*
{{ 10 * 5 }}
50
Divide
/
{{ 10 / 5 }}
2
Round to decimals
.toFixed(n)
{{ (10.567).toFixed(2) }}
“10.57”
Round to integer
Math.round()
{{ Math.round(10.5) }}
11
Round up
Math.ceil()
{{ Math.ceil(10.1) }}
11
Round down
Math.floor()
{{ Math.floor(10.9) }}
10
Absolute value
Math.abs()
{{ Math.abs(-10) }}
10
Maximum
Math.max()
{{ Math.max(10, 20, 5) }}
20
Minimum
Math.min()
{{ Math.min(10, 20, 5) }}
5
Working with Dates and Time (Luxon Basics)
n8n includes Luxon for date handling. You don’t need to import anything – it’s already available.
Most expressions are one-liners. But sometimes you need variables or multi-step logic. That’s where IIFE (Immediately Invoked Function Expression) comes in.
The syntax looks intimidating but it’s just wrapping your code in a function that executes immediately:
{{
(() => {
// Your code here with variables
return result;
})()
}}
The (() => { ... })() wrapper creates a function scope where you can use variables and multiple statements.
This calculates the number of months between two dates and rounds the result. Too complex for a single line, but IIFE makes it possible without switching to a Code node.
The ternary operator format is condition ? valueIfTrue : valueIfFalse. You can chain them, but it gets messy fast. For more than 2-3 conditions, use IIFE:
When to stop and use Code node instead: If your IIFE exceeds 10 lines or needs loops, switch to Code node. Expressions should remain readable at a glance. Complex logic belongs in Code nodes where you have proper debugging and structure.
Common Expression Errors (And How to Fix Them)
Error Message
Common Cause
Quick Fix
“Cannot read property of undefined”
Accessing missing/null field
Use optional chaining: {{ $json.field?.property }}
“Referenced node is unexecuted”
Node hasn’t run yet
Check workflow execution order
“Invalid syntax”
Typo, missing bracket, wrong mode
Use expression editor, check brackets balance
“Expression not recognized”
Missing {{ }} wrapper or Fixed mode
Toggle to Expression mode, add wrapper
Webhook data shows undefined
Not accounting for body nesting
Use {{ $json.body.field }} instead of {{ $json.field }}
Error “Cannot read property of undefined”
What it means: You’re trying to access a field that doesn’t exist in your data.
Common causes
The field name is misspelled (JavaScript is case-sensitive)
The data structure is different than you expected
Previous node returned empty results
The field is optional and doesn’t always exist
Fix with optional chaining
// Before (breaks if email is missing):
{{ $json.user.email }}
// After (returns undefined instead of crashing):
{{ $json.user?.email }}
// With fallback value:
{{ $json.user?.email || 'no-email@example.com' }}
The ?. operator stops execution if user doesn’t exist instead of throwing an error. The || operator provides a default value if the field is undefined or empty.
Error “Referenced node is unexecuted”
What it means: You’re trying to access data from a node that hasn’t run yet.
This happens when you reference $node["Node Name"] but that node is on a workflow path that didn’t execute. Maybe it’s after an IF node and went down the other branch. Maybe it’s disabled.
Fix: Restructure your workflow so the node you’re referencing always executes before you try to access its data. Or handle the missing data with optional chaining.
Real World Workflow Examples (Building Blocks With n8n Expressions)
Scenario 1: Processing Contact Form Submissions
// Webhook receives:
{
"body": {
"firstName": "john",
"lastName": "doe",
"email": "JOHN@EXAMPLE.COM",
"message": " Need help with automation "
}
}
// Clean and format for CRM:
// Full name (capitalize first letter of each)
{{ $json.body.firstName.charAt(0).toUpperCase() + $json.body.firstName.slice(1) + " " + $json.body.lastName.charAt(0).toUpperCase() + $json.body.lastName.slice(1) }}
// "john doe" → "John Doe"
// Or use n8n's custom method (cleaner):
{{ ($json.body.firstName + " " + $json.body.lastName).toTitleCase() }}
// Email (lowercase, no spaces)
{{ $json.body.email.toLowerCase().trim() }}
// "JOHN@EXAMPLE.COM" → "john@example.com"
// Message (remove extra whitespace)
{{ $json.body.message.trim() }}
// " Need help with automation " → "Need help with automation"
Scenario 2: E-Commerce Order Processing
// Calculate order total with tax
{{ ($json.subtotal * 1.08).toFixed(2) }}
// Tag high-value orders
{{ $json.subtotal >= 1000 ? 'VIP' : 'Standard' }}
// Format order date for display
{{ DateTime.fromISO($json.orderDate).toFormat('MMM dd, yyyy') }}
// "2026-01-18T00:00:00.000Z" → "Jan 18, 2026"
// Create order summary using template literal
{{ `Order #${$json.orderId} - ${$json.items.length} items - Total: $${$json.total}` }}
// "Order #12345 - 3 items - Total: $156.99"
Scenario 3: Multi Node Data Combination
// Combine user data from earlier node with current order
{{ $node["Get User"].json.name + " ordered " + $json.productName }}
// "Sarah ordered Premium Plan"
// Mix webhook data with database lookup
{{ $json.body.email + " (Customer ID: " + $node["Database Query"].json.customerId + ")" }}
// "user@example.com (Customer ID: C-001)"
```
These scenarios mirror actual automation workflows. You can adapt these patterns directly to your use cases.
## Quick Reference Cheat Sheet
Keep these patterns handy for quick lookup.
**Data Access:**
```
Current node data: {{ $json.fieldName }}
Nested data: {{ $json.parent.child.field }}
Spaces in names: {{ $json["Field Name"] }}
Previous node: {{ $node["Node Name"].json.field }}
All items: {{ $input.all() }}
First item: {{ $input.first().json.field }}
```
**Common Transformations:**
```
Uppercase: {{ $json.text.toUpperCase() }}
Lowercase: {{ $json.text.toLowerCase() }}
Combine text: {{ $json.first + " " + $json.last }}
Template literal: {{ `${$json.first} ${$json.last}` }}
Array length: {{ $json.items.length }}
Join array: {{ $json.tags.join(", ") }}
Current date: {{ $now.toFormat('yyyy-MM-dd') }}
Round money: {{ ($json.price * 1.1).toFixed(2) }}
Extract email: {{ $json.text.extractEmail() }}
Title case: {{ $json.name.toTitleCase() }}
```
**Conditionals:**
```
Simple if/else: {{ $json.active ? 'Yes' : 'No' }}
Check exists: {{ $json.email?.includes("@") }}
Fallback value: {{ $json.name || 'Unknown' }}
```
**Advanced:**
```
JMESPath query: {{ $jmespath($json, "users[*].email") }}
JMESPath filter: {{ $jmespath($json, "items[?price > `50`]") }}
Object keys: {{ Object.keys($json.settings) }}
Merge objects: {{ { ...$json.user, ...$json.prefs } }}
What’s Next? From Expression to Workflows
You now know how to access data $json, transform strings and arrays, handle dates, query JSON with JMESPath, work with objects, and fix common errors. That covers 90% of the expressions you’ll write.
The next level is knowing when to stop using expressions. If you’re writing 15-line IIFEs with nested loops, that logic belongs in a Code node. Expressions keep workflows readable. Code nodes handle complex transformations.
Practice with simple expressions first. Grab a field, make it uppercase, combine two fields. Build muscle memory. Then tackle transformations – filtering arrays, calculating totals, formatting dates. The syntax clicks after you’ve used it a few times.
Master data extraction before moving to transformations. Get comfortable accessing fields, then start manipulating them. That progression works better than trying to learn everything at once.
When you’re ready to build complete workflows with these expressions, start here
When I started my n8n journey, credentials weren’t an issue for me, it was all plug and play and everything seemed to work, then I stopped workflow for few weeks, and started the workflow again, then I got credential error. I was like! what! what just happened, then I had to re-authenticate to make it work.
But, what if this issue constantly happen for n8n workflow builder occasionally, that’s pure annoyance. so the builder needs to rinse and repeat. So OAuth2 clients needs to be refresh it’s tokens.
This ain’t a bug in your workflow. It’s how OAuth2 access tokens work by default. They expire after 7 days. Everytime they expire, you need to manually re-authenticate it (This method considered as a fix, but it’s not efficient). For automated workflows that should run without any interruptions.
I’ve seen lots of users had posted their frustration “credentials expiring” in reddit, and community forums.
Alright, let’s fix this credentials expiring issue right now.
Why Do We Need Access & Refresh Tokens?
The Refresh token in Google OAuth2 for n8n acts as a long-term key that allows n8n to generate a short-term access key (access token) without requiring you to log in manually every hour.
Two Types of Tokens
Access token: This is the actual key n8n uses to read your sheets or add google calendar events. For security purposes this key will expire after 1 hour.
Refresh token: This is a special key stored securely in your n8n database. It has only one job, ask Google to get a new token if current refresh token expired.
Next thing, we are gonna see how does this actually works.
When you setting up credentials, This is what will happen.
Initial AuthProcess: You click “Sign in with Google” in n8n.
Exchange: n8n sends your consent code to Google and requests access_type=offline
Storage: Google sends back an Access token and a Refresh token. n8n saves both.
Auto Renewal
n8n attempts to run a node (e.g. Google sheet read)
if Google replies 401 unauthorized (Token expired), n8n automatically pauses
Click the button to Publish App (push to production)
Note: You don’t need to submit your app for verification if you are only using it for yourself (using your own email address). However, you’ll see a warning screen says Google hasn’t verified this app, but you click advanced > Go to App(unsafe) to bypass it.
Configuration Checklist
To ensure the Refresh token mechanism work correctly, Your Google Cloud Console settings must match this criteria.
Redirect URL: Must be exactly https://[YOUR-N8N-DOMAIN]/rest/oauth2-credential/callback or https://localhost:5678/rest/oauth2-credential/callback
Scopes: if you add any new scopes (e.g., adding Gmail to an existing Drive credentials), the old Refresh token may become invalid. You must re-authenticate to generate a new one that covers all the updated scopes.
If you’re setting up n8n credentials for the first time or working with other services, Check our n8n credentials and service guide for step by step instructions across all the major platforms.
Your workflow should now run uninterrupted. No more weekly re-authenticate cycles.