
How to Build Multi-Level Approval Workflows in n8n
TL;DR: n8n has no built-in multi-level approval node. You have three options: chain multiple Send-and-Wait nodes together (simple but rigid), store approval state in a database and poll for changes (flexible but you are building infrastructure), or delegate the level logic to a managed platform (less n8n complexity, adds a dependency). This post walks through all three patterns with concrete node layouts and trade-offs. I am building Humangent around the third approach; it is in private beta and I am honest below about what exists today versus what is on the roadmap.
An n8n multi-level approval workflow routes a single request through sequential sign-offs. Team lead approves, then manager approves, then VP approves. Every level must say yes before the action executes. Any rejection at any level stops the chain. This is the pattern you see in purchase orders, content publishing, data changes in regulated environments, and customer communications with high-value accounts.
n8n does not ship a node for this. The community forum has threads going back years asking for Power Automate-style multi-stage approvals. The answer is always the same: build it yourself. This post covers the three ways to do that.
Multi-level approval is not escalation
These two concepts get confused constantly, and the confusion leads to broken workflows. They need to be separated clearly before we go further.
Multi-level approval means multiple people sign off in sequence. Team lead reviews first. If they approve, the request moves up to the manager. If the manager approves, it moves to the VP. All three must approve. The chain moves upward through levels of authority.
Escalation means a request gets reassigned when someone does not respond. If the team lead does not act within four hours, the request goes to a backup reviewer at the same level. Escalation is a fallback mechanism for inaction. It does not advance the request to a higher authority.
You often need both in the same workflow. A request might require three levels of approval, and each level might have an escalation rule for when the assigned reviewer is unavailable. But they are different problems that require different logic. Conflating them is one of the most common mistakes I see in n8n approval builds.
Where multi-level approval is unavoidable
Not every approval needs multiple levels. A single reviewer works fine for most day-to-day decisions. But certain business processes genuinely require sequential sign-offs because each level evaluates something different.
Purchase requests with amount thresholds. A $300 tool subscription needs a team lead's confirmation that the team actually needs it. A $15,000 annual contract also needs finance to confirm budget availability. A $60,000 vendor deal adds the VP to verify strategic alignment. The number of required levels scales with the dollar amount because each level checks something the levels below cannot.
Content publication chains. A writer produces a draft. An editor reviews accuracy and tone. Legal checks for compliance issues. Each reviewer brings domain knowledge the others lack. Removing a level is not faster -- it is a gap in the review.
Customer communications on sensitive accounts. A support agent drafts a response to a strategic account. The account manager reviews the commitments being made. For the largest accounts, a director confirms the response aligns with the relationship strategy. The cost of a bad message scales with the account value.
Data changes in regulated industries. An analyst proposes a correction to production records. The team lead validates the logic. The data owner confirms authorization and scope. In healthcare and financial services, this chain is a compliance requirement, not a preference.
The thread connecting all of these: each level contributes a perspective that the other levels do not have, and skipping a level creates risk that outweighs the time saved.
Pattern 1: Sequential approval chains in pure n8n
This is where most people start. It uses only built-in n8n nodes. The idea is linear: notify Level 1, wait for their response, check if they approved, notify Level 2, wait, check, notify Level 3, wait, check, execute.
You have two implementation choices inside this pattern:
- Native
Send and Wait for Responseat each level (Slack, Gmail, Teams, Telegram, Discord, WhatsApp, Chat). One node handles notification + pause + timeout per level. Simpler -- around 10 nodes for a 3-level chain -- but each level's recipient is a hardcoded channel-specific ID (SlackU08XXXX, Telegramchat_id, Teams object ID). Fine for fixed reviewers; awkward when the right reviewer depends on the request. - HTTP Request + Wait for Webhook at each level (the layout shown below). More plumbing but more flexibility -- you can pick the reviewer dynamically per request, send to any system, and shape the notification however you want. The 15-25 node count comes from this variant.
The walkthrough below uses the HTTP Request + Wait for Webhook variant because it's the one teams reach for once "always send to Alice" stops being true. If your chain has fixed reviewers, swap each HTTP Request + Wait for Webhook pair for a single Send-and-Wait node and the rest of the structure stays the same.
Node layout for a three-level chain
Trigger
-> Set node (prepare request payload)
-> HTTP Request (send notification to Level 1 reviewer)
-> Wait for Webhook (pause for Level 1 response)
-> IF node (did Level 1 approve?)
-> Yes:
-> Set node (append Level 1 decision and comments)
-> HTTP Request (send notification to Level 2 reviewer)
-> Wait for Webhook (pause for Level 2 response)
-> IF node (did Level 2 approve?)
-> Yes:
-> Set node (append Level 2 decision)
-> HTTP Request (send notification to Level 3 reviewer)
-> Wait for Webhook (pause for Level 3 response)
-> IF node (did Level 3 approve?)
-> Yes: Execute the final action
-> No: Rejection notification (Level 3 rejected)
-> No: Rejection notification (Level 2 rejected)
-> No: Rejection notification (Level 1 rejected)
Each level requires at least three nodes: one to send the notification, one to pause for the response, and one to branch on the decision. A three-level chain typically runs 15 to 20 nodes once you include data preparation and rejection handling.

Wiring the webhooks
Each Wait for Webhook node needs a unique path. A pattern that works:
/approval/{{$json.requestId}}/level-1
/approval/{{$json.requestId}}/level-2
/approval/{{$json.requestId}}/level-3
The notification sent to each reviewer contains two URLs -- one for approve, one for reject -- that target the corresponding webhook. When the reviewer clicks, the workflow picks up from that specific Wait node.
Between levels, you accumulate the approval record using Set nodes. By the time the request reaches Level 3, the payload should include every prior decision with timestamps and comments:
{
"requestId": "PO-2026-0412",
"originalRequest": {
"type": "purchase",
"amount": 18000,
"vendor": "Acme Analytics",
"description": "Annual platform license renewal"
},
"approvals": [
{
"level": 1,
"reviewer": "[email protected]",
"decision": "approved",
"comments": "Team confirmed they use this daily",
"timestamp": "2026-04-10T09:15:00Z"
},
{
"level": 2,
"reviewer": "[email protected]",
"decision": "approved",
"comments": "Budget allocated in Q2 plan",
"timestamp": "2026-04-10T14:42:00Z"
}
]
}
Where this pattern holds up
It works when the chain is short and stable. Two levels, maybe three. The same reviewers every time. The workflow does not change often. You are comfortable reading a 20-node canvas and knowing exactly where each branch leads.
For a single workflow with a fixed two-level approval, this is the right choice. There is no external dependency, no database to maintain, and the logic is visible in the workflow itself.
Where it breaks down
Adding or removing a level means surgery. A fourth level adds another five or six nodes. Removing a level means carefully disconnecting and deleting without breaking adjacent branches. Every structural change touches the entire workflow.
No centralized view of pending approvals. If eight requests are in flight across different levels, there is no single place to see them. You are opening individual execution logs in the n8n interface, which was not designed for this.
Timeout handling doubles the node count. Each Wait for Webhook needs a timeout. When a timeout fires, you need branching logic: retry? Escalate to a backup? Auto-reject? Each timeout branch adds two or three more nodes per level. A 20-node workflow becomes 35 nodes.
Conditional routing creates combinatorial sprawl. If some requests need two levels and others need four, you build separate branches for each path. Three request types with different routing rules can push you past 50 nodes.
This pattern has a ceiling. One or two static workflows is fine. Beyond that, the maintenance burden grows faster than the value.
If Pattern 1's ceiling is where your current chain is sitting, join the early-access list for Humangent. Pattern 3 below is what's being built, and early users are shaping how levels-as-config actually behaves under real chains.
Pattern 2: Database-driven approval with polling
This pattern separates the approval state from the n8n workflow. Instead of encoding each level as a chain of nodes, you write the approval state to a database and let a separate polling workflow manage level transitions.
The components
You need three things working together.
A database schema. Postgres, Supabase, MySQL, even Airtable if you prefer no-code storage. Two tables handle the core logic:
CREATE TABLE approval_requests (
id UUID PRIMARY KEY,
workflow_execution_id TEXT,
request_type TEXT,
request_data JSONB,
current_level INT DEFAULT 1,
max_levels INT,
status TEXT DEFAULT 'pending',
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE approval_decisions (
id UUID PRIMARY KEY,
request_id UUID REFERENCES approval_requests(id),
level INT,
reviewer TEXT,
decision TEXT,
comments TEXT,
modifications JSONB,
decided_at TIMESTAMP DEFAULT NOW()
);
A main workflow that creates the request and waits for the final outcome:
Trigger
-> Code node (generate request ID, calculate required levels based on request properties)
-> Postgres node (INSERT into approval_requests)
-> HTTP Request (notify Level 1 reviewer)
-> Wait for Webhook (store resume URL in the database row)
-> Postgres node (read final decision and full approval chain)
-> IF node -> Execute or reject
A polling workflow that runs on a schedule and handles level transitions:
Schedule Trigger (every 2-5 minutes)
-> Postgres node (SELECT requests with new decisions at current level)
-> For each request:
-> Is current level approved and more levels remain?
Yes: increment current_level, notify next reviewer
-> Is current level approved and it was the final level?
Yes: set status to 'approved', hit the resume webhook
-> Was current level rejected?
Yes: set status to 'rejected', hit the resume webhook
Reviewers interact through a form -- either an n8n Form Trigger workflow or a lightweight web page -- that writes their decision to the approval_decisions table.
What this approach gives you
Dynamic chains without touching nodes. Adding a level means changing the max_levels value. Conditional routing is a Code node that calculates how many levels a specific request needs based on its properties. No workflow restructuring required.
Queryable state. You can run SQL against the database to see all pending approvals, identify bottleneck reviewers, measure average approval time per level, and generate reports. This visibility is absent in Pattern 1.
Resilience. If n8n restarts, the state lives in the database. The polling workflow resumes checking. Nothing is lost.
What it costs
You are maintaining a small application. A database schema, a polling workflow, a reviewer-facing form, and the glue logic connecting them. This is infrastructure, not configuration.
Polling introduces latency. A five-minute polling interval means up to five minutes between a reviewer clicking approve and the next level being notified. More frequent polling means more database queries and more n8n executions burning through your quota.
You build the reviewer experience yourself. The form needs to display the original request, all prior approvals, comments, and modifications. It needs to let reviewers add their own comments and edits. Building a clear, usable form for this in n8n is achievable but takes real effort to get right.
This pattern makes sense when you have five or more workflows sharing approval infrastructure, when you need reporting on approval metrics, or when your chains vary in length based on request properties. For a single workflow with fixed levels, it is more machinery than the problem demands.
Pattern 3: A managed platform (the direction I'm building)
Patterns 1 and 2 both leave you maintaining approval infrastructure -- either as node wiring inside each workflow or as a small application alongside it. The third approach moves the multi-level logic out of n8n entirely. A managed human-in-the-loop platform becomes the thing that holds the chain. You send one request from n8n, the platform handles level progression, reviewer notifications, timeouts, and the reviewer interface, and the workflow resumes when the chain resolves.
This is the approach I'm building Humangent around. Humangent is in private beta right now. Early users are shaping which patterns ship first, so I want to be clear about what is a shipped feature versus a design direction before I walk through how this pattern is supposed to work.
The core idea: levels as config, not nodes
The design goal for Pattern 3 is to turn the multi-level chain into a declarative piece of configuration instead of a wiring diagram. In Pattern 1 the chain lives in n8n nodes. In Pattern 2 it lives in your database schema and polling logic. In Pattern 3 it lives in a config object that the platform interprets.
From the n8n side, the workflow stays minimal:
Trigger
-> Code node (prepare request payload, build levels array)
-> HTTP Request node (POST to the HITL platform)
-> Wait for Webhook (platform calls back when the chain completes)
-> IF node (check final decision)
-> Execute or reject
Five nodes, regardless of whether the chain is two levels or four. The shape I'm designing for on the request payload looks roughly like this -- treat it as a sketch of the intended API, not a stable contract:
{
"title": "Purchase Order: Acme Analytics ($18,000)",
"body": "Annual platform license renewal for the data team...",
"levels": [
{ "assignee": "team-lead", "timeout": "4h" },
{ "assignee": "finance", "timeout": "24h" },
{ "assignee": "vp", "timeout": "48h" }
],
"callbackUrl": "https://your-n8n.com/webhook/approval-complete/PO-2026-0412"
}
The design intent behind that shape:
- Each level sees prior approvals. When Level 2 opens the request, they should see what Level 1 decided, the comments they left, and any edits they made -- not just a "approved" flag.
- Progression between levels is handled by the platform. When a level approves, the next level is notified automatically. The n8n workflow stays paused at a single Wait node the entire time.
- Escalation happens within a level, not between levels. If the assigned reviewer at Level 2 does not respond by the timeout, the request should route to a backup reviewer at the same level. The chain does not skip ahead to Level 3 just because Level 2 is slow. This separation matters -- it is the same distinction between multi-level approval and escalation from earlier in the post.
- The final callback returns the full decision chain. n8n gets one webhook hit with every decision, every edit, every timestamp, so the downstream workflow can branch on the outcome and log the audit trail.
What this pattern is trying to solve
Reading Pattern 1 and Pattern 2, the same pain points keep showing up: the chain logic bleeds into places that are not good at holding it. n8n canvases are not great at representing variable-length chains. Ad-hoc databases are not great at being a reviewer-facing product. The reviewer experience in both patterns tends to be an afterthought -- Slack messages, raw forms, execution logs.
The Pattern 3 bet is that if you move the chain into a purpose-built platform, three things get easier at once:
- Workflow complexity stays flat. Adding a fourth level is adding an object to the
levelsarray, not rewiring nodes. - The reviewer gets a real interface. One place to see pending decisions, full context, and prior approvals -- instead of scrolling Slack.
- The audit trail comes from the system of record, not from reconstructing execution logs.
Those are the design goals. Whether Humangent delivers each one well is what the private beta is for.
Honest status
Humangent is in private beta. The shape above is the shape I'm designing for; the beta is where I'm finding out which parts of it are right and which need to change. If you join the waitlist and end up in the beta, your feedback on the levels config, the reviewer interface, and the callback contract is what determines which patterns ship first. If you need a fully shipped, battle-tested system today, the honest answer is Pattern 1 or Pattern 2 in pure n8n -- they are not glamorous, but they work and you control everything.
The trade-off that applies to any managed platform
Whichever managed platform you pick for this pattern, you are adding an external dependency. Your approval chain runs through a third-party service. If the service goes down, pending approvals stall. For some teams that is a dealbreaker. For others, the reduction in n8n complexity is worth it, especially when the alternative is a 35-node workflow or a custom database someone has to keep running.
One thing worth planning for regardless of platform choice: make sure the integration stays on standard n8n nodes (HTTP Request and Wait for Webhook) rather than a custom community node. If the service disappears or you outgrow it, swapping two nodes is a much easier exit than ripping out a plugin.
Conditional routing: different requests, different chains
Most real-world approval workflows do not send every request through the same number of levels. The chain length depends on the request itself. This applies to all three patterns above -- here is how each one handles it.
A common example with purchase approvals:
| Amount | Required levels |
|---|---|
| Under $500 | Team lead only |
| $500 -- $5,000 | Team lead, then finance |
| $5,000 -- $50,000 | Team lead, finance, then VP |
| Over $50,000 | Team lead, finance, VP, then CFO |
In Pattern 1, you implement this with Switch or IF nodes at the start of the workflow. Each branch is a separate approval chain of different length. Three request types with different rules means maintaining three parallel chains in one workflow. The canvas gets wide, and changes touch multiple branches.
In Pattern 2, you calculate max_levels dynamically in a Code node when inserting the request. The polling workflow already checks whether current_level has reached max_levels, so variable-length chains require no extra logic. This is one of the strongest arguments for the database approach.
In Pattern 3, you build the levels array dynamically in a Code node before the API call:
const amount = $input.first().json.amount;
const levels = [{ assignee: "team-lead", timeout: "4h" }];
if (amount >= 500) {
levels.push({ assignee: "finance", timeout: "24h" });
}
if (amount >= 5000) {
levels.push({ assignee: "vp", timeout: "48h" });
}
if (amount >= 50000) {
levels.push({ assignee: "cfo", timeout: "72h" });
}
return { levels, ...$input.first().json };
All the routing logic lives in a single Code node. No parallel branches, no duplicated chains.
Conditional routing is where Pattern 1 hits a wall. If you are building it and find yourself creating multiple parallel chains for different request types, that is a strong signal to evaluate Pattern 2 or 3.
Handling modifications between levels
The simple approve/reject model misses a common scenario: approval with conditions.
A team lead reviews a $18,000 purchase order and approves it with the note: "Go ahead, but negotiate the renewal price below $15,000." Now the finance reviewer at Level 2 needs to see three things: the original request, the team lead's approval, and the specific condition attached to it. If finance only sees "approved by team lead," they are making their decision with incomplete information.
In Pattern 1, you add a third webhook path per level for "approve with modifications." The reviewer submits their changes via a form that posts to this webhook. Your Set node between levels merges the original data, the decision, and the modifications into a single payload. The notification template for the next level needs conditional formatting to display modifications when they exist. This works for two levels. At three or four levels with potential modifications at each, the template logic becomes fragile and hard to debug.
In Pattern 2, the database handles this naturally. The approval_decisions table has a modifications column. The reviewer form reads all prior decisions and displays the full history. The rendering logic is separate from the workflow logic, which keeps both manageable.
In Pattern 3, the design goal is for the platform to carry modifications forward automatically -- each level's decision, comments, and edits included in the context shown to the next level, without the workflow builder maintaining the display logic. How cleanly that works in practice is one of the things the private beta is stress-testing.
Picking the right pattern
Pattern 1 (sequential Send-and-Wait) fits when you have one or two workflows with a fixed chain of two or three levels. The chain rarely changes. You do not need cross-workflow visibility or approval metrics. You are willing to maintain 15-25 nodes per workflow.
Pattern 2 (database-driven polling) fits when you have multiple workflows sharing approval infrastructure. You want reporting on approval times, rejection rates, and bottleneck reviewers. You already operate a database. Your chains vary in length based on request properties.
Pattern 3 (managed platform) fits when the approval complexity is growing faster than your appetite to maintain it, and you would rather consume a platform than build one. Humangent is the option I'm building -- human controls for n8n automations, in private beta, shaped by early users.
These patterns are not mutually exclusive. A common progression: start with Pattern 1 for your first approval workflow, move to Pattern 2 when you hit four or five workflows, and evaluate Pattern 3 when you realize you are spending more time on approval infrastructure than on the automations that depend on it.
Common questions
Can I mix approval levels that use different notification channels? Yes. In all three patterns, each level's notification is independent. Level 1 can notify via Slack, Level 2 via email, and Level 3 via a dedicated inbox. In Pattern 1, each level has its own HTTP Request node pointing at a different channel. In Patterns 2 and 3, the notification channel is a per-level config value.
What happens if a reviewer is out of office? This is where escalation enters the picture. In Pattern 1, you build timeout handling per Wait node -- when the timeout fires, you branch to notify a backup reviewer. In Pattern 2, the polling workflow checks for stale requests and reassigns them. In Pattern 3, the design intent is for the platform to handle escalation automatically based on per-level timeout rules. Remember: escalation reassigns within a level. It does not advance the request to the next level.
Can a higher-level reviewer override a rejection from a lower level? Not in a standard sequential chain -- a rejection at any level halts the entire request. If you need override capability, you would add a separate "appeal" path that routes rejected requests to the next level for a second opinion. This is a distinct workflow branch, not part of the standard multi-level pattern.
How do I handle requests that need different reviewers based on department or region? This is routing logic that sits before the approval chain. A Code or Switch node at the start of the workflow determines which reviewers are assigned to each level based on the request's department, region, or other properties. In Pattern 3, the intent is to assign levels to roles rather than specific people and let the platform resolve the role to the right individual.
Is there a performance limit on the number of levels? Practically, most organizations cap at three or four levels. Beyond that, the delay becomes counterproductive. Technically, Pattern 1 gets unwieldy past three levels. Patterns 2 and 3 handle deeper chains without structural issues, but the real bottleneck is reviewer response time, not system capacity.
Related guides
- How to build an n8n approval workflow — single-level basics first
- n8n approval timeouts and escalation — timeouts at each level of the chain
- n8n payment and refund approval workflows — dual approval applied to financial flows
- Audit trails for n8n AI agents — recording each sign-off in a defensible chain
If you need multi-level approvals in n8n without building 20-node chains, Humangent is the AI approval workflow for n8n — sequential sign-offs as configuration, not branched If/Wait nodes; escalation when a level stalls; every level captured in the audit. Join the waitlist at humangent.io. Free during private beta, no credit card.