AI EngineeringZero to ProductionHome·About·What’s new·Contact
No-Code Agentic AI · Chapter N1

Agentic Workflows with n8n

Not every agent needs a Python file. n8n is an open-source, self-hostable workflow tool where you wire nodes on a canvas — triggers, apps, an LLM, tools — into an agent that runs on a schedule or a webhook. This chapter builds one, and shows exactly where the drag-and-drop maps onto the loop you already coded by hand.

⏱️ ~50 min🧩 Hands-on🎯 Beginner→Expert
🌱 Start here — from zero No-code agentic AI, from scratch — build working AI agents by connecting boxes on a canvas — no code required.

No-code tools let you build automations by dragging nodes (a trigger, an AI step, an action) and wiring them together, instead of writing a program. They're the fastest way to ship a useful AI workflow and to prototype an idea before coding it. This section covers the main platforms and when each fits.

The words you'll hear (in plain terms):

TermWhat it actually means
no-code / low-codebuilding software by configuration and visual wiring instead of writing code.
nodeone step in a workflow — a trigger, an AI call, a database write.
triggerthe event that starts a workflow (a form submit, a schedule, a new email).
workflowthe connected chain of nodes from trigger to result.
webhooka URL that lets one app poke another to start a workflow.

What you need before starting:

  • No programming needed — this is the one section you can do with zero code.
  • A free account on the tool you want to try (n8n, Zapier, Make, or Flowise).
  • Having read what an agent is helps but isn't required.

New to the topic? Read this box, then take the chapters in order — each section is tagged essential → expert so you always know the depth you're at.

Learning objectives

  • Explain what n8n is and when a visual workflow beats hand-written code.
  • Name the core building blocks: trigger, nodes, connections, expressions, credentials.
  • Build an LLM-powered workflow, then upgrade it to a true AI Agent node with tools + memory.
  • Map every canvas concept back to the agent loop from Chapter 4 / C2.
  • Handle credentials, errors, and self-hosting safely.

What n8n is — and when to reach for it essential

Here's a scene you'll recognize the moment you work with a team. Someone asks for a small automation: "when a customer emails support, classify it and drop billing tickets into Slack." You could write a Python service — an API key in an env var, a webhook handler, a deploy, a place to run it, logs when it breaks. Three days later it works, and then the person who actually owns support wants to tweak the wording of the Slack message and can't, because it's code they can't read, on a server they can't touch. The automation becomes your permanent chore. That mismatch — a five-minute idea trapped behind an engineer — is the exact gap n8n was built to close.

The mental model is simplest if you picture a factory conveyor belt. A part drops on at one end (the trigger), moves past a line of stations that each do one small thing to it (the nodes), and comes off the end finished. You don't write the belt; you arrange the stations and draw the wires between them. That visual arrangement is the program — which is why a non-engineer can look at the canvas, see "email → classify → route to Slack," and change the last station without breaking the first. The workflow is legible because it's a picture, not a wall of syntax.

Now the precise definition, which will land because you have the picture: n8n is a workflow-automation platform where an automation is a directed graph of nodes, and you build it by dragging boxes onto a canvas and connecting them instead of writing code. Two properties make it worth a chapter of its own. First, it's self-hostable — you can run the whole thing on your own machine or server (the Docker lab below does exactly that), so your credentials and data never leave infrastructure you control. Second, it's fair-code, not "no-code-only": when the visual nodes run out, you can drop into a Code node and write real JavaScript or Python, so you never hit a hard ceiling. That combination is why it sits between click-only tools and hand-written services rather than competing head-on with either.

The common mistake is treating this as "the easy version for people who can't code." It isn't — the value is speed and ownership, not avoidance. A senior engineer reaches for n8n precisely because gluing five SaaS apps together with retries, a scheduler, and a shareable canvas is faster here than in Python, and hands it to a team that can maintain it. The skill this chapter teaches is knowing which side of the line a given problem falls on — and the table just below is your first cut at that judgement.

n8n ("nodemation") is a fair-code, self-hostable workflow-automation platform. You build automations by placing nodes on a canvas and connecting them; each node does one thing (fire on a schedule, call an API, run an LLM, branch on a condition) and passes data to the next. It sits between "click-only" tools like Zapier (N2) and writing everything in code: more power and control than the former, far less boilerplate than the latter.

less control ← the spectrum → more control Click-only tools Zapier · Make (N2) easy, low ceiling n8n visual nodes + Code-node escape hatch Hand-written code Python (C2) full power, most work self-hostable · fair-code · no hard ceiling n8n lives in the middle. It gives more power and ownership than click-only tools, with far less boilerplate than writing everything in code — and a Code node so you never hit a wall.
🗺️ How to read this diagram

Read it left-to-right along the top arrow: the further right you go, the more control you get — and the more work it costs you.

  • The left box is click-only tools (Zapier, Make): quick to start but you hit a ceiling when the logic gets non-trivial.
  • The right box is hand-written code (Python, the C2 path): full power, but you own the boilerplate — webhooks, deploys, retries, logging.
  • The highlighted centre box is n8n: visual node wiring for speed, plus a Code node escape hatch so you never hit a hard ceiling.
  • The green band underneath names the two properties that make n8n worth its own chapter — it is self-hostable (your keys stay on your infra) and fair-code (drop into real JS/Python when the nodes run out).

In short: n8n trades a little of code's raw power for a lot less boilerplate — and unlike click-only tools, it has no hard ceiling.

Where this module fitsYou've built agents in code (Chapters 4 & C2). This module is the no/low-code path to the same outcomes. The value isn't "avoiding code" — it's shipping internal automations fast, and giving non-engineers a surface they can maintain. You'll still recognize every concept.
Reach for n8n when…Reach for code (C2) when…
Gluing many SaaS apps together (Gmail, Slack, Sheets, a CRM)The logic is complex, custom, or performance-critical
The workflow should be visible & editable by a teamYou need full testing, version control, and CI
You want a scheduler/webhook + retries for freeYou're building a product, not an internal automation
You want to self-host and own your dataFine-grained control of the agent loop matters (safety gates)

The building blocks essential

Before you build anything, you have to be able to name the parts — and the good news is there are only five, and you already met their ideas in the agent chapters. The reason to learn the vocabulary first is practical: when a workflow misbehaves, the fix is almost always "the wrong kind of block, or a block wired to the wrong upstream data." If you can't tell a trigger from a node from a connection, you can't debug it. So spend two minutes here and the rest of the chapter reads itself.

Think of the whole thing as a sentence with grammar. The trigger is the subject that starts everything ("when an email arrives…"); the nodes are the verbs, each doing one action ("…classify it, then post it"); the connections are the word order that says which verb runs next; and the items flowing along the connections are the thing being acted on, carried forward station to station. A workflow reads left to right like a sentence, and every valid workflow has exactly one subject — one active trigger — because something has to decide when the sentence gets spoken.

Here is the one detail that trips up every beginner, so internalize it now: data does not flow as a single value — it flows as a list of items. Each item is a small bundle of JSON like { "message": "I was charged twice" }, and n8n runs each node once per item automatically. That's why a workflow that classifies one email works unchanged on a batch of fifty — the node body describes what to do to one item, and n8n loops for you. Beginners who assume "one run = one value" get baffled the first time a node emits three items and the next node runs three times; once you know items are a list, that behaviour is obvious instead of spooky.

The remaining two blocks are the plumbing that makes the sentence real. An expression — the {{ $json.field }} syntax — is how one node reaches back and reads a field a previous node produced, so stations can pass specifics forward, not just vague data. A credential is a stored, encrypted secret (an API key, an OAuth token) that a node references by name rather than containing — a separation that matters enormously the day you export or share a workflow, because the secret stays behind. Keep these five names in view as you read the diagram and table below; everything else in n8n is just which node you drop into this same skeleton.

Trigger LLM / Agent IF / branch App data (JSON "items") flows left → right along the connections schedule / webhook Slack / Sheets… A workflow is a directed graph of nodes. A trigger starts it; each node transforms the JSON items it receives and passes them on. Connections are the edges. That's the whole model — everything else is which node you drop in.
🗺️ How to read this diagram

This is the entire mental model of n8n in one picture: a workflow is just boxes (nodes) wired left-to-right, and data flows along the wires. Nothing here is code — you build this by dragging boxes onto a canvas and drawing the connections.

  • Read it left to right, following the arrows. The leftmost box is the Trigger — the event that starts the workflow (the small label underneath says schedule / webhook, i.e. a timer or an incoming web request).
  • Each box after it is a node that does one job: the LLM / Agent node calls a language model, the IF / branch node makes a yes/no decision, and the final App node takes an action in another tool (Slack / Sheets…).
  • The arrows are connections — the wires you draw. The caption at the top says data travels along them as JSON "items": small bundles of named fields (like { "message": "..." }) that each node reads, changes, and passes on.
  • That's the whole system: trigger → node → node → action. Everything else in n8n is just which box you drop in and how you configure it.

In short: A node is a step, a connection is an arrow, and the JSON "items" are the data riding along the arrows. If you can read this graph, you can read any n8n workflow.

ConceptWhat it is
Trigger nodeStarts the workflow — a schedule, an incoming webhook, a new email, a chat message. Every workflow has exactly one active start.
NodeOne unit of work: call an API, run an LLM, transform data, branch. 400+ app integrations ship built-in.
ConnectionThe wire between nodes. Data flows along it as a list of JSON items.
ExpressionInline templating to pull data from earlier nodes: {{ $json.email }}, {{ $node["Webhook"].json.body }}.
CredentialA stored, encrypted secret (API key, OAuth token) referenced by nodes — never pasted into the node itself.

Lab N1.1 · Get n8n running essential

The fastest way to lose an afternoon with a new tool is the install. You find three tutorials, one wants a global npm package, another a cloud signup, a third a from-source build, and each leaves a slightly different mess on your machine. n8n sidesteps all of that with a single Docker command — and it's worth understanding why that command is the recommended path, not just copy-pasting it, because the reasoning is the same one that makes n8n worth self-hosting in the first place.

Here's the key idea in plain terms: self-hosting means the software runs on infrastructure you own, so your secrets and data stay with you. An n8n workflow holds live API keys and can read your email, write to your CRM, and post to your Slack. When you run it yourself, none of that ever passes through someone else's server. The trade is that you become the operator — you're responsible for keeping it running, patched, and locked down (which is exactly what the danger callout below hammers home). n8n Cloud exists for people who'd rather pay to skip that responsibility; the Docker path below is for people who want the control.

The lab uses Docker because a container is a pre-packaged, isolated copy of an app plus everything it needs to run, so "install n8n" collapses into "download and run this one image" — no version conflicts with whatever else is on your machine. The one subtlety that beginners miss, and that the walkthrough spells out, is the split between the container (disposable — the --rm flag throws it away when you stop it) and the volume (persistent — the -v n8n_data flag is a named box on your disk where your workflows and credentials actually live). Get that distinction and you'll never panic when the container disappears; forget it and you'll think you lost all your work on the first restart. Follow the three steps in order: run the container, add your Anthropic credential once so every later node can reference it by name, and drop a Manual Trigger so you can click-to-run while you build.

the container · disposable n8n container --rm → deleted on stop the running process, port 5678 gone after Ctrl-C the volume · persistent -v n8n_data named box on your disk workflows + credentials here survives restarts writes data → restart the exact same command → the volume re-attaches → your work is still there Container vs volume: the one distinction to keep straight. The container is disposable (the --rm flag deletes it on stop); the named volume persists on disk, so your workflows and credentials survive a restart.
🗺️ How to read this diagram

Read it as two boxes side by side: the left is the throwaway part, the right is the part that keeps your work.

  • The left box is the n8n container — the running process on port 5678. The --rm flag means it is deleted when you stop it (the red strip: "gone after Ctrl-C").
  • The right box is the volume (-v n8n_data) — a named box on your disk where your workflows and credentials actually live. The green strip says it survives restarts.
  • The arrow between them is the container writing your data into the volume, so the data outlives the process that created it.
  • The payoff line at the bottom is the "aha": re-run the exact same command and the volume re-attaches — which is why your account and workflows are still there after a restart.

In short: The container is disposable; the volume is your save file. Lose the container and you lose nothing; lose the volume and you lose everything.

Example code for learning — review, test, and adapt it before running against real or production systems. Commands can create, change, or delete resources. See the Terms & Disclaimer.
Lab N1.1
  1. Run it locally with Docker (self-host is the whole point):
    shelldocker run -it --rm --name n8n -p 5678:5678 \
      -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n

    Open http://localhost:5678 and create the owner account. (n8n Cloud is the hosted alternative if you'd rather not self-host.)

  2. Add your Anthropic credential. In Credentials → New, add an Anthropic API key once. Every LLM node references it by name — the key lives encrypted in n8n, not in a workflow.
  3. Create a blank workflow and drop a Manual Trigger so you can run it on demand while building.
▶ How this works

This one command downloads n8n and starts it on your own machine, so you get the full visual editor in your web browser with nothing to install by hand. docker is a tool that runs pre-packaged apps in isolated "containers"; here it runs the official n8n package. The backslashes (\) at the line ends just mean "this command continues on the next line" — it's really one command.

  1. docker run starts a container. -it keeps it attached to your terminal so you can watch its logs, and --rm auto-deletes the container when you stop it (your data is kept separately — see below).
  2. --name n8n gives the container a friendly name so you can refer to it later.
  3. -p 5678:5678 is port mapping: it connects port 5678 inside the container to port 5678 on your computer. That's why the next step opens http://localhost:5678 — localhost means "this machine".
  4. -v n8n_data:/home/node/.n8n is a volume: it stores n8n's data (your workflows, credentials) in a named box on your disk that survives even though --rm throws the container away. Without this, you'd lose your work on restart.
  5. The last part, docker.n8n.io/n8nio/n8n, is the image — the address of the n8n package to download and run.

What the output means: Your terminal prints startup logs and an "Editor is now accessible" line. Opening http://localhost:5678 in a browser shows the n8n setup screen where you create the owner account — you're now running n8n locally.

Try this: Stop it with Ctrl-C, then run the exact same command again. Because of the -v n8n_data volume, your account and workflows are still there — proof the volume, not the container, holds your data.

Self-hosting is real infrastructureA self-hosted n8n holds live credentials and can call anything. Put it behind auth, use HTTPS, keep it updated, and never expose the editor to the public internet. Treat it like any service that holds secrets (Chapter 6, T1).

Lab N1.2 · An LLM in a workflow intermediate

Everyone's first instinct with an LLM node is to type a prompt straight into it and hit run — and it works, once. Then you realize the whole point of a workflow is that the input isn't fixed: a real email, a form submission, a Slack message arrives, and the model has to see that text, not the sentence you hardcoded while testing. The moment you feel that gap — "how does the incoming data actually reach the prompt?" — you've hit the one concept that makes n8n's LLM nodes different from pasting into a chat window. This lab exists to make that wiring concrete on the smallest possible example: a one-node classifier.

The bridge is the expression, and the cleanest way to understand it is as a mail-merge slot. When you write a form letter, you don't retype the name for every recipient — you write "Dear {{first_name}}," and the system fills the slot from each record. n8n's {{ $json.message }} is exactly that: a slot in your prompt that n8n replaces, just before sending, with the message field of the item flowing in. So the prompt template stays fixed while the data behind it changes on every run. $json means "the item arriving at this node" and .message reaches into its named field — the visual, canvas-side twin of writing item["message"] in Python.

Two working habits in this lab are worth naming because they're the same discipline you learned coding, just relocated to the canvas. Pinning sample data on the trigger freezes one known input ({"message": "I was charged twice!"}) so you can re-run the workflow repeatedly and get a comparable result each time — it's a fixed test case, the visual version of the classifier tests from Chapter 2, and it's how you tune a prompt without a live event firing every time. And clicking a node to inspect its exact JSON in and out is n8n's genuinely killer feature: instead of guessing why a downstream step misbehaves, you see the precise data that entered and left every station. That per-node visibility is the thing seasoned n8n users lean on hardest; get in the habit early. Now build the two-node chain in the lab and watch a support message turn into a single clean label.

Start simple: a workflow that classifies an incoming message. This is the visual twin of Chapter 2's classifier.

fixed prompt template …{{ $json.message }} the slot incoming item {"message":"…twice!"} actual prompt sent Message: I was charged twice! Claude → label billing n8n fills the slot from the item just before sending — the template never changes An expression is a mail-merge slot. The prompt template stays fixed; {{ $json.message }} is a slot n8n fills from the incoming item right before the call — so the same node handles every different message.
🗺️ How to read this diagram

Follow the arrows left-to-right: two things on the left merge in the middle, and the model turns the merged text into one clean label on the right.

  • The top-left box is your fixed prompt template — the instruction plus a slot written {{ $json.message }}. It never changes between runs.
  • The bottom-left box is the incoming item: the JSON bundle flowing into the node, e.g. {"message":"…twice!"}. This is the part that does change on every run.
  • The two arrows meet at the centre box — the actual prompt sent. n8n has replaced the slot with the item's message field, so the model sees real text, not the placeholder.
  • The green box is the payoff: because you told the model to reply with only a label, it returns a single clean word (billing) a later node can branch on.

In short: {{ $json.message }} pulls a field off the incoming item into a fixed template — the canvas twin of writing item["message"] in Python.

Lab N1.2
  1. Manual Trigger → Anthropic Chat Model node. Set the model to claude-opus-4-8 and the prompt:
    node: promptClassify this support message as bug | feature | billing | other.
    Reply with only the label.
    
    Message: {{ $json.message }}

    The {{ }} expression pulls message from the trigger's data.

  2. Pin sample data on the trigger ({"message": "I was charged twice!"}) so you can run repeatedly with the same input — the visual version of a fixed test case (Chapter 2).
  3. Execute the workflow. Click a node to inspect the exact JSON in and out — n8n's killer feature for debugging.
▶ How this works

This is the text you type into the LLM node's prompt field — the instruction sent to Claude every time the workflow runs. It does the same job as Chapter 2's classifier, but you configure it in a box on the canvas instead of in Python. The header node: prompt is just a label telling you where this text lives.

  1. The first two lines are the instruction: tell the model exactly what to do (classify as bug | feature | billing | other) and exactly how to answer (reply with only the label). Being this specific is what keeps the output clean enough for a later node to act on.
  2. The blank line then Message: gives the model the actual text to classify. Keeping the instruction and the data visually separate helps the model not confuse the two.
  3. {{ $json.message }} is an n8n expression — the {{ }} marks a slot that n8n fills in before sending. $json means "the JSON item coming into this node" and .message pulls its message field. So if the trigger's item is {"message": "I was charged twice!"}, the model actually receives Message: I was charged twice!.

What the output means: The node outputs a single word — for the charged-twice example, billing — because you told it to reply with only the label. Click the node in n8n to see the exact JSON that went in and came out.

Try this: Change the pinned sample data to {"message": "the app keeps crashing"} and re-run. The {{ $json.message }} slot picks up the new text and the label should switch to bug — no other change needed.

Structured output still matters"Reply with only the label" is prompt-level control. For anything a later node parses, use a Structured Output parser node (or an LLM node's JSON mode) so downstream branches get typed fields, not free text — the same lesson as Chapter 2, enforced on the canvas.

Lab N1.3 · From workflow to AI Agent intermediate

You'll hit the wall the moment you ask your classifier a question it can't answer from the prompt alone. "What's today's EUR/USD rate?" A plain Chat Model node will happily invent a number — confidently, plausibly, wrongly — because all it can do is answer from what it already knows. The fix isn't a better prompt; it's a fundamentally different node that can go get the rate before answering. That leap — from a model that talks to a model that acts — is the single most important idea in this chapter, and in n8n it's a one-box upgrade: swap the Chat Model node for an AI Agent node.

If you built the agent in Chapter 4 by hand, you already know the machine hiding inside that box; if you didn't, here's the whole thing in one loop. An agent runs a cycle: reason about the goal, decide it needs a tool, call the tool, observe the result, then reason again — repeating until the task is done. In code (C2) you wrote that while loop yourself: send the messages, check whether the model asked for a tool, run it, append the result, resend. The AI Agent node is that loop, packaged. You no longer write the round-trips; you supply the three ingredients and the node runs the cycle internally. That's the exact same mental model you already own, with the plumbing hidden — which is why "if you understand Chapter 4, you understand this node" is not a slogan but a literal fact.

The three ingredients you attach are worth understanding as roles, because getting one wrong is the usual reason a canvas agent underperforms. The Chat Model is the brain — the actual Claude model doing the reasoning and choosing what to do next. Memory (the Window Buffer Memory node) is the notebook — it carries the conversation across turns, so a chat agent remembers what was said three messages ago instead of starting cold each time; it's the same messages list you appended to and resent by hand. And tools are the toolbox — sub-nodes the agent may call, each with a description that the model reads to decide when to reach for it. That last point is the one beginners underestimate: the agent selects tools purely from their descriptions, so a vague description ("does stuff with orders") makes it pick wrong, while a sharp one ("look up an order's status by its numeric id") makes it pick right. Read the diagram as manager-plus-resources, then build the agent in the lab and — crucially — ask it something that forces a tool call, so you can watch the loop actually fire.

A single LLM node answers. The AI Agent node acts — it runs the tool-use loop for you: given tools and a goal, it decides which tools to call, calls them, reads results, and loops until done. This is Chapter 4's agent, as one node.

AI Agent node Chat Model Memory Tools (HTTP…) the Agent node runs the reason → call tool → observe loop internally The AI Agent node is the loop, packaged. You attach a chat model (the brain), optional memory (conversation state), and tools (sub-nodes it can call). The node handles the round-trips you wrote by hand in C2 — you just supply the pieces.
🗺️ How to read this diagram

This diagram shows how the single AI Agent node is assembled from smaller pieces you plug into it. Where a plain LLM node just answers, the Agent node acts: it can call tools, read the results, and keep going until the task is done — all handled inside the one node.

  • The top box, "AI Agent node", is the orchestrator. The three arrows dropping down from it show the sub-parts you attach underneath it on the canvas.
  • Chat Model (bottom-left) is the brain — the actual Claude model that does the thinking and decides what to do next.
  • Memory (middle) stores the conversation so far, so a chat agent remembers what was said on earlier turns instead of starting fresh each message.
  • Tools (HTTP, Sheets…) (bottom-right) are the actions the agent is allowed to take — call an API, read a spreadsheet, etc. The agent chooses which to use based on each tool's description.
  • The caption underneath — the Agent node runs the reason → call tool → observe loop internally — is the key point: you supply the pieces, and the node does the repeated back-and-forth (the "agent loop") for you.

In short: Think of the Agent node as a manager and the boxes below as its resources: a brain (model), a notebook (memory), and a toolbox (tools). You hand it those three things; it figures out the rest.

Lab N1.3
  1. Drop an AI Agent node. Attach the Anthropic Chat Model as its model.
  2. Give it a tool. Attach an HTTP Request tool (e.g. a weather API) or a Google Sheets tool. Write a clear tool description — the agent picks tools by description, exactly like C2.
  3. Add memory (a Window Buffer Memory node) so a chat-triggered agent remembers the conversation across turns.
  4. Trigger it with a Chat Trigger and talk to it. Ask something that requires the tool and watch the node call it, read the result, and answer.
Same concepts, new surfaceModel = the brain. Tools = functions it can call (with descriptions that drive selection). Memory = the messages history you resent by hand in C2. The AI Agent node hides the loop plumbing — but it's the identical mental model. If you understand Chapter 4, you understand this node.

The canvas ↔ code Rosetta Stone intermediate

There's a quiet anxiety that creeps in when you switch tools: the fear that you're learning something disposable. "If I get good at dragging n8n boxes, does any of it transfer the day I have to write the real thing in Python?" This section exists to kill that fear outright, because the honest answer is: a visual workflow is a program, and every box maps to a line of code you already know how to write. The canvas isn't a different world — it's the same computation wearing a different coat. Seeing the correspondence directly is what turns n8n from a black box into a tool you trust.

The table below is a literal dictionary between the two. A trigger node is your script's entry point — the event or handler that starts main(). A Chat Model node is one client.messages.create(...) call. A tool sub-node with a description is one entry in the tools=[...] array you pass to the API. The whole AI Agent node is the while tool-use loop you hand-wrote in C2. A Memory node is the messages list you keep appending to and resending. An expression {{ $json.x }} is just reading a field off the previous step's result. And a credential is a key loaded from an environment variable or secrets manager. Nothing on the left has no equivalent on the right; that's the point.

Read this two ways, because it pays off in both directions. Left-to-right, it lets you prototype on the canvas and know exactly what you'd write if you later needed the control of code — the visual version is a runnable spec. Right-to-left, it lets an experienced coder read any n8n workflow at a glance, because they can silently translate each box into the code they'd have written. That fluency — moving between canvas and code without friction — is precisely the FDE judgement this whole no-code module is building toward: knowing not just how to build each way, but which way a given problem deserves. Exercises 4 through 6 make you exercise that translation until it's automatic.

canvas · n8n node code · C2 / Ch 4 Trigger node entry point / handler Chat Model node messages.create(...) AI Agent node while tool-use loop Tool sub-node + desc entry in tools=[...] Expression {{ $json.x }} read prev result field every box on the left is exactly one line you already know how to write on the right A workflow is a program in disguise. Each canvas box maps one-to-one to code you already know — so nothing you learn on the canvas is disposable.
🗺️ How to read this diagram

Read it row by row, left to right: each blue box on the left is an n8n node, and the arrow points to its exact code equivalent on the right.

  • The left column is the canvas — the boxes you drag onto the n8n workspace (trigger, Chat Model, AI Agent, tool, expression).
  • The right column is the code you'd write by hand in C2 / Chapter 4 — the entry point, a messages.create(...) call, the while loop, a tools=[...] entry, a field read.
  • The arrows are the dictionary: each is a one-to-one translation. The two purple boxes (AI Agent node ↔ the whole while tool-use loop) are the biggest single mapping — one box replaces the entire hand-written loop.

In short: Nothing on the canvas is a dead end — every box is a line of code you can write yourself the day you need the control.

n8n conceptCode equivalent (C2 / Ch 4)
Trigger nodeThe entry point / event that starts your script
Chat Model nodeclient.messages.create(model=...)
Tool sub-node + descriptionA tool definition in the tools=[...] array
AI Agent nodeThe whole while tool-use loop
Memory nodeThe messages list you append to and resend
Expression {{ $json.x }}Reading a field off a previous step's result
CredentialKey loaded from env / secrets manager

Common pitfalls advanced

The dangerous thing about a tool that ships a working demo in ten minutes is that it makes the hard parts feel optional. They aren't — they're just deferred. Every failure in the table below is one that doesn't show up on your laptop and only surfaces when the workflow meets real traffic, real secrets, or a public URL. The pattern worth internalizing is that "no-code" removed the boilerplate, not the responsibilities: your n8n instance still holds live API keys and still takes real actions in real systems, so it inherits every operational concern a hand-written service would have.

Read the pitfalls as three families. The first is secrets handling: pasting an API key into a node field bakes it into the workflow, so it leaks the instant you export, share, or version that workflow — which is the entire reason credentials exist as a separate, encrypted, reference-by-name mechanism. The second is exposure: an editor or webhook reachable from the open internet with no auth is a standing invitation, since anyone who finds the URL can drive your workflows and the credentials behind them; the fix is auth, HTTPS, and restricted webhook paths, exactly as you'd protect any secret-holding service. The third is trusting the model too much: routing a branch on free-text LLM output breaks the day the model phrases the label differently, and a vague tool description makes an agent pick the wrong tool — both fixed by forcing structure (a typed field, a sharp description) so downstream logic reads data, not prose.

The last row is the philosophy that ties them together, and it's the one to carry out of this chapter: "no-code" is not "no-ops." The workflow needs error handling on flaky nodes (retries plus an error branch), monitoring so you notice when it silently stops, and the same security posture you'd give a production API. Treating a canvas as somehow exempt from operations is how a convenient internal automation quietly becomes an unmonitored liability holding live credentials. Skim the fixes below now; you'll apply every one of them in the reliability and industry exercises on the practice ladder.

the risk (fails only in production) the fix (same as any real service) 1 · secrets API key pasted into a node field Credential, referenced by name 2 · exposure public editor / webhook, no auth auth, HTTPS, restricted webhooks 3 · over-trust branch on free-text LLM output typed field + sharp tool descs "No-code" removed the boilerplate, not the responsibilities. Every pitfall falls into one of three families — secrets, exposure, over-trusting the model — and each red risk has a green fix that is exactly what you'd do for any secret-holding, live-acting service.
🗺️ How to read this diagram

Read it row by row: each red box on the left is a way the workflow fails, and the arrow points to the green box that fixes it.

  • The three numbered rows group every pitfall into three families: secrets handling, public exposure, and over-trusting the model.
  • The red boxes (left) are the mistakes that look fine on your laptop and only bite under real traffic — a pasted key, an open webhook, a branch reading raw model prose.
  • The green boxes (right), reached by each arrow, are the fixes: reference secrets as named Credentials, put the instance behind auth and HTTPS, and force structure (typed fields, sharp tool descriptions) so downstream logic reads data, not text.

In short: Sort any n8n failure into secrets, exposure, or over-trust — then apply that row's fix, which is the same discipline you'd give any production service.

PitfallFix
Pasting API keys into a node fieldUse a stored Credential — never inline secrets
Exposing the editor / webhook publicly with no authPut n8n behind auth + HTTPS; restrict webhook URLs
Passing free-text LLM output into a branchUse a structured-output parser so fields are typed
Vague tool descriptionsDescribe when to use each tool — the agent selects by description
No error handling on flaky nodesAdd an Error Trigger workflow / node retry settings
Treating "no-code" as "no-ops"It still holds secrets & runs live actions — secure & monitor it

Exercises advanced

Exercise N1.1 — Ticket router

Context: The cleanest way to branch on an LLM's judgement is to make it emit a typed field, then let a deterministic node route on that field rather than on free text.

Your task: Build a Webhook Trigger → Chat Model (classify) → IF workflow that sends billing messages to a Slack channel and everything else to a Google Sheet.

Requirements:

  • The Chat Model emits a structured object like {"category":"...","summary":"..."}
  • A structured-output parser types the category field so the branch is not reading free text
  • The IF node compares {{ $json.category }} to "billing"
  • Two output branches wire to two different app nodes (Slack vs Sheet)

💡 Hint: Parse the model output into a typed field first; branching on parsed JSON is reliable in a way branching on raw text never is.

Show hint

Have the LLM node emit {"category": "...", "summary": "..."}. In the IF node compare {{ $json.category }} to "billing". Two output branches, two app nodes.

Exercise N1.2 — Tool-using agent

Context: An agent that quotes a number without calling a tool is hallucinating. The execution view is where you prove a tool actually fired.

Your task: Give an AI Agent node an HTTP Request tool that hits a public API (e.g. currency rates) and ask it a question that requires the tool.

Requirements:

  • The agent has exactly one tool: an HTTP Request against a real public endpoint
  • The question can only be answered correctly by calling that tool
  • Confirm from the execution view that the tool node actually ran
  • The returned number matches the API, not a plausible invention

💡 Hint: Trust the execution trace, not the answer text — a right-looking number with no tool call in the trace is a hallucination.

Exercise N1.3 — Map it back

Context: The point of the whole no-code module is seeing that a canvas agent and a hand-written tool loop are the same machine. Placing them side by side makes the equivalence concrete.

Your task: Take your Exercise N1.2 agent and write the equivalent Python using the C2 manual tool loop — a model, one tool definition, and the while loop.

Requirements:

  • One tool definition mirrors the HTTP tool the n8n agent used
  • A while loop drives model call → tool call → feed result back until the model stops
  • The code path and the canvas path answer the same question the same way
  • Note which canvas piece maps to which line of the loop

💡 Hint: The canvas hides the while loop the Agent node runs for you — your job is just to make that loop visible in code.

🪜 Practice ladder beginner → industry

Six graded exercises, easy to real-world. Try each before opening its solution.

Exercise 1 · Name the four building blocksBeginner

Context: Every n8n workflow is the same shape: a trigger fires, nodes transform an array of items, and connections wire them together. Before you build anything, you have to be able to name those parts.

Your task: Sketch the node graph for “when a webhook fires, call an LLM, post the result to Slack” and label each block as a trigger, an action node, or a connection.

Requirements:

  • Exactly one trigger (Webhook) that emits a single item from the request body
  • A linear chain: trigger → LLM node → Slack node
  • Each connection passes an array of items ({json:{...}}) downstream
  • State what each node reads off the item and what it writes back

💡 Hint: The whole workflow is one trigger plus nodes wired to pass an item array forward — name the role of each of the three blocks, don't add any.

Show solution

The linear three-node graph the lesson starts from:

[ Webhook Trigger ]  --items-->  [ OpenAI / LLM node ]  --items-->  [ Slack node ]
      trigger                        action (transform)              action (output)

Roles:

trigger      Webhook        starts the run, emits 1 item with the request body
node (LLM)   LLM Chat       reads item.json.text, writes item.json.reply
node (out)   Slack          posts item.json.reply to a channel
connection   the wires      pass ITEMS (an array of {json: {...}}) downstream

Every n8n workflow is this shape: one trigger, then nodes wired to pass an array of items forward.

Exercise 2 · Design an LLM-in-a-workflow with a prompt fieldIntermediate

Context: An LLM node earns its place only once its prompt is wired to real incoming data. n8n does that mapping with {{ $json.field }} expressions rather than code.

Your task: Configure the LLM node's parameters as JSON: a system prompt, a user field mapped from the incoming item, and a defined place for the output to land.

Requirements:

  • System prompt is a fixed role instruction
  • The user prompt pulls fields from the item via {{ $json.subject }} style expressions
  • The value is prefixed with = so n8n evaluates it as an expression rather than sending it literally
  • A low temperature keeps triage output stable
  • Name the downstream field the model output is read from

💡 Hint: The = prefix is the load-bearing detail — without it the {{ }} tokens are passed as plain text instead of being resolved.

Show solution

n8n maps upstream data with {{ $json.field }} expressions. The node config:

{
  "node": "OpenAI Chat Model",
  "parameters": {
    "system": "You are a support triage assistant. Reply in one sentence.",
    "prompt": "={{ $json.subject }}\n\n{{ $json.body }}",
    "temperature": 0.2
  }
}

Downstream nodes then read the model output. A common correctness detail: the = prefix marks the whole string as an expression, so {{ }} is evaluated rather than sent literally.

The mapping {{ $json.subject }} pulls the field from the current item — this is the "canvas" equivalent of item["subject"] in code.

Exercise 3 · Workflow to AI Agent: add toolsAdvanced

Context: The jump from an LLM node to an AI Agent node is the jump from one fixed call to a model that chooses tools in a loop. The agent selects tools purely from their descriptions.

Your task: Upgrade the LLM node to an AI Agent node that can call an HTTP “lookup order” tool and a calculator; draw the agent graph and write the tool declarations.

Requirements:

  • The Agent node is fed a Chat Model node plus one connected node per tool
  • Attach a Window Buffer Memory node so the agent keeps turn context
  • Each tool carries a name and a description that tells the model when to use it
  • The HTTP tool maps its input into the URL via {{ $fromAI('id') }}
  • The model — not the wiring — decides which tool fires

💡 Hint: Write the tool descriptions as if the model is your only reader; a vague description is why an agent picks the wrong tool.

Show solution

The AI Agent node takes a model + a set of connected tool nodes; it decides which to call. Graph:

                    +-- [ Tool: HTTP "get_order" ] (GET /orders/{id})
[ Chat Trigger ] -- [ AI Agent ] --+
       |                +-- [ Tool: Calculator ]
       |            (model: Chat Model node)
       +-- memory: [ Window Buffer Memory ]

Each tool is declared with a name + description the agent reasons over:

{
  "tool": "get_order",
  "description": "Look up an order by its id. Input: {\"id\": \"string\"}",
  "method": "GET",
  "url": "https://api.example.com/orders/{{ $fromAI('id') }}"
}

The jump from LLM node to Agent node is exactly the jump from "one fixed call" to "model chooses tools in a loop" — the description text is what the model uses to pick.

Exercise 4 · The canvas ↔ code Rosetta StoneExpert

Context: A visual workflow is still a program: its data type is “array of items” and its steps are nodes. Making that mapping explicit is what lets you move fluently between canvas and code.

Your task: Produce the canvas↔code correspondence table, then back it with a n8n Code node snippet that transforms items the way a Set node would.

Requirements:

  • Map trigger, node, connection, item, Set, IF, and Loop each to a code equivalent
  • An item ({json:{...}}) corresponds to a dict in a list
  • The Code node returns a new items array, one dict per item
  • It renames/derives fields (e.g. builds a full name, lowercases an email) exactly as a Set node would

💡 Hint: Return items.map(...) from the Code node — the array of items is the workflow's one data type, so a transform is just a map over it.

Show solution

Canvas ↔ code correspondence:

canvas concept        code equivalent
-------------------   ---------------------------------
trigger               the event/handler that starts main()
node                  a function step
connection            passing the return value onward
item ({json:{...}})   a dict in a list
Set node              building/renaming dict fields
IF node               an if/branch
Loop / Split In Batches   a for-loop over items

An n8n Code node (JavaScript) doing what a Set node does, one item at a time:

// n8n Code node -- runs once, returns the new items array
return items.map(item => ({
  json: {
    fullName: item.json.first + " " + item.json.last,
    email: item.json.email.toLowerCase()
  }
}));

Seeing the mapping makes the canvas legible: a workflow is a program whose data type is "array of items" and whose steps are nodes.

Exercise 5 · Reliability: retries, error branch, dedupProfessional

Context: Production workflows fail in three ways: transient errors, permanent errors, and replays. Each has its own remedy — retries, an error branch, and an idempotency key.

Your task: Design the per-node reliability settings, a dedicated error-output branch, and a dedup-key strategy that makes the workflow safe to replay.

Requirements:

  • Enable Retry On Fail with a max-tries count and a wait between attempts for flaky nodes
  • Set On Error to continue via the error output and wire it to an alert + mark-failed branch
  • Build a stable dedup key per event (e.g. source:id) and skip an event whose key was already seen
  • Retries cover transient faults; the error branch covers permanent ones; the dedup key covers replays
  • Show the dedup logic skipping the second copy of a duplicate event

💡 Hint: Retry, error branch, and dedup are three separate defenses — don't collapse them; the dedup key must be stable across replays, not random per run.

Show solution

Per-node reliability settings + a dedicated error branch:

node settings (HTTP / LLM node):
  Retry On Fail:     true
  Max Tries:         3
  Wait Between:      2000 ms (exponential-ish)
  On Error:          "Continue (error output)"  -> wire error output to a branch

error branch:
  [ node error output ] --> [ Slack: alert #ops ] --> [ Set: mark row failed ]

Idempotency via a dedup key so replays don't double-process. The logic, modeled offline:

seen = set()
def process(item):
    key = f"{item['source']}:{item['id']}"     # stable per-event key
    if key in seen:
        return "skip (duplicate)"
    seen.add(key)
    return "process"

evts = [{"source":"webhook","id":"A"}, {"source":"webhook","id":"A"},
        {"source":"webhook","id":"B"}]
print([process(e) for e in evts])   # ['process', 'skip (duplicate)', 'process']

Retry handles transient failures; the error branch handles permanent ones; the dedup key makes the whole workflow safe to replay.

Exercise 6 · Self-hosted n8n support-triage workflowIndustry scenario

Context: A real support-triage workflow has to classify inbound email, route by category, and auto-reply or escalate — while logging every path so the behavior is auditable and the LLM never acts irreversibly alone.

Your task: Design the end-to-end workflow from an IMAP Email Trigger to a Switch that routes by category, ending in an append to an audit log; give the full node graph and the routing table.

Requirements:

  • An AI Agent classifies each email into {category, urgency, needs_human}
  • A Switch on {{ $json.category }} routes billing, howto, bug, and a default escalation path differently
  • The LLM only classifies; deterministic nodes perform the actions
  • The default/unknown branch escalates to a human channel
  • Every path appends a row to a Google Sheet audit log (id, category, action, timestamp)

💡 Hint: Keep the irreversible actions on deterministic nodes downstream of the classifier — the model decides the label, the wiring decides the act.

Show solution

The complete graph:

[ IMAP Email Trigger ]
      |
[ AI Agent: classify {category, urgency, needs_human} ]
      |
[ Switch on {{ $json.category }} ]
   |-- billing  --> [ HTTP: create billing ticket ] --> [ Reply: template ]
   |-- howto    --> [ LLM: draft answer ] --> [ IF urgency=high? ] --> reply / queue
   |-- bug      --> [ Jira: create issue ] --> [ Reply: ack ]
   |-- default  --> [ Slack #support: escalate to human ]
      |
[ Append row to Google Sheet: audit log (id, category, action, ts) ]

The routing table the Switch encodes, modeled offline:

def route(cls):
    table = {"billing": "auto-ticket + templated reply",
             "howto":   "LLM draft, human review if urgent",
             "bug":     "file Jira + ack"}
    return table.get(cls["category"], "escalate to human")

print(route({"category": "billing"}))
print(route({"category": "unknown"}))   # -> escalate

The design principle: the LLM classifies, deterministic nodes act, and every path logs — so behavior is auditable and the LLM never takes an irreversible action alone.

✓ Checkpoint — you can move on when you can…

  • Say when a visual workflow beats hand-written code, and when it doesn't.
  • Name trigger, node, connection, expression, and credential and what each does.
  • Build an LLM workflow with pinned test data and inspect the JSON at each node.
  • Turn it into an AI Agent node with a model, a tool, and memory.
  • Map every canvas concept to its code equivalent from C2 / Chapter 4.
🏗️ Toward the capstoneNot every part of a real deployment needs to be bespoke code. A DevOps agent's notification and routing glue — "when a deploy fails, summarize the logs and post to the on-call channel" — is often faster and more maintainable as an n8n workflow than more Python. Knowing where the no-code boundary sits is an FDE skill (Chapter 7). See the FDE method →

Knowledge check check yourself

✓ Knowledge check

In n8n, what is the whole workflow model in terms of triggers, nodes, and connections, and what travels along the connections?

Show answer
A workflow is a directed graph: exactly one active trigger starts it, each node does one unit of work (call an API, run an LLM, branch, transform), and connections are the edges between them. Data flows along the connections as a list of JSON items that each node reads, transforms, and passes on.
✓ Knowledge check

What does the AI Agent node add over a plain Chat Model node, and what three pieces do you attach to it?

Show answer
A plain LLM/Chat Model node just answers; the AI Agent node acts — it runs the reason→call-tool→observe loop internally (the Chapter 4 / C2 loop, packaged), deciding which tools to call and looping until done. You attach a chat model (the brain), optional memory (conversation state), and tools (sub-nodes with descriptions that drive selection).
© 2026 studybydoing.in · AI Engineering: Zero to Production · All rights reserved. · About · Privacy Policy · Terms · Contact
Educational content, provided as-is and without warranty. Code samples are examples — review, test, and adapt them before using in production. See the Terms of Use & Disclaimer. Use at your own risk.
© studybydoing.in